1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import static java.util.Collections.emptySet;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.util.ArrayMap;
24 import android.util.ArraySet;
25 import android.util.ExceptionUtils;
26 
27 import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
28 
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.function.BiConsumer;
36 import java.util.function.Function;
37 import java.util.function.Predicate;
38 import java.util.stream.Stream;
39 
40 /**
41  * Utility methods for dealing with (typically {@code Nullable}) {@link Collection}s
42  *
43  * Unless a method specifies otherwise, a null value for a collection is treated as an empty
44  * collection of that type.
45  */
46 public class CollectionUtils {
CollectionUtils()47     private CollectionUtils() { /* cannot be instantiated */ }
48 
49     /**
50      * @see Collection#contains(Object)
51      */
contains(@ullable Collection<T> collection, T element)52     public static <T> boolean contains(@Nullable Collection<T> collection, T element) {
53         return collection != null && collection.contains(element);
54     }
55 
56     /**
57      * Returns a list of items from the provided list that match the given condition.
58      *
59      * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
60      * {@link Stream} instance
61      */
filter(@ullable List<T> list, java.util.function.Predicate<? super T> predicate)62     public static @NonNull <T> List<T> filter(@Nullable List<T> list,
63             java.util.function.Predicate<? super T> predicate) {
64         ArrayList<T> result = null;
65         for (int i = 0; i < size(list); i++) {
66             final T item = list.get(i);
67             if (predicate.test(item)) {
68                 result = ArrayUtils.add(result, item);
69             }
70         }
71         return emptyIfNull(result);
72     }
73 
74     /**
75      * @see #filter(List, java.util.function.Predicate)
76      */
filter(@ullable Set<T> set, java.util.function.Predicate<? super T> predicate)77     public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
78             java.util.function.Predicate<? super T> predicate) {
79         if (set == null || set.size() == 0) return emptySet();
80         ArraySet<T> result = null;
81         if (set instanceof ArraySet) {
82             ArraySet<T> arraySet = (ArraySet<T>) set;
83             int size = arraySet.size();
84             for (int i = 0; i < size; i++) {
85                 final T item = arraySet.valueAt(i);
86                 if (predicate.test(item)) {
87                     result = ArrayUtils.add(result, item);
88                 }
89             }
90         } else {
91             for (T item : set) {
92                 if (predicate.test(item)) {
93                     result = ArrayUtils.add(result, item);
94                 }
95             }
96         }
97         return emptyIfNull(result);
98     }
99 
100     /** Add all elements matching {@code predicate} in {@code source} to {@code dest}. */
addIf(@ullable List<T> source, @NonNull Collection<? super T> dest, @Nullable Predicate<? super T> predicate)101     public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest,
102             @Nullable Predicate<? super T> predicate) {
103         for (int i = 0; i < size(source); i++) {
104             final T item = source.get(i);
105             if (predicate.test(item)) {
106                 dest.add(item);
107             }
108         }
109     }
110 
111     /**
112      * Returns a list of items resulting from applying the given function to each element of the
113      * provided list.
114      *
115      * The resulting list will have the same {@link #size} as the input one.
116      *
117      * This is similar to {@link Stream#map} but without the overhead of creating an intermediate
118      * {@link Stream} instance
119      */
map(@ullable List<I> cur, Function<? super I, ? extends O> f)120     public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
121             Function<? super I, ? extends O> f) {
122         if (isEmpty(cur)) return Collections.emptyList();
123         final ArrayList<O> result = new ArrayList<>();
124         for (int i = 0; i < cur.size(); i++) {
125             result.add(f.apply(cur.get(i)));
126         }
127         return result;
128     }
129 
130     /**
131      * @see #map(List, Function)
132      */
map(@ullable Set<I> cur, Function<? super I, ? extends O> f)133     public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
134             Function<? super I, ? extends O> f) {
135         if (isEmpty(cur)) return emptySet();
136         ArraySet<O> result = new ArraySet<>();
137         if (cur instanceof ArraySet) {
138             ArraySet<I> arraySet = (ArraySet<I>) cur;
139             int size = arraySet.size();
140             for (int i = 0; i < size; i++) {
141                 result.add(f.apply(arraySet.valueAt(i)));
142             }
143         } else {
144             for (I item : cur) {
145                 result.add(f.apply(item));
146             }
147         }
148         return result;
149     }
150 
151     /**
152      * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
153      *
154      * Calling this is equivalent (but more memory efficient) to:
155      *
156      * {@code
157      *      filter(
158      *          map(cur, f),
159      *          i -> { i != null })
160      * }
161      */
mapNotNull(@ullable List<I> cur, Function<? super I, ? extends O> f)162     public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
163             Function<? super I, ? extends O> f) {
164         if (isEmpty(cur)) return Collections.emptyList();
165         List<O> result = null;
166         for (int i = 0; i < cur.size(); i++) {
167             O transformed = f.apply(cur.get(i));
168             if (transformed != null) {
169                 result = add(result, transformed);
170             }
171         }
172         return emptyIfNull(result);
173     }
174 
175     /**
176      * Returns the given list, or an immutable empty list if the provided list is null
177      *
178      * This can be used to guarantee null-safety without paying the price of extra allocations
179      *
180      * @see Collections#emptyList
181      */
emptyIfNull(@ullable List<T> cur)182     public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
183         return cur == null ? Collections.emptyList() : cur;
184     }
185 
186     /**
187      * Returns the given set, or an immutable empty set if the provided set is null
188      *
189      * This can be used to guarantee null-safety without paying the price of extra allocations
190      *
191      * @see Collections#emptySet
192      */
emptyIfNull(@ullable Set<T> cur)193     public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) {
194         return cur == null ? emptySet() : cur;
195     }
196 
197     /**
198      * Returns the given map, or an immutable empty map if the provided map is null
199      *
200      * This can be used to guarantee null-safety without paying the price of extra allocations
201      *
202      * @see Collections#emptyMap
203      */
emptyIfNull(@ullable Map<K, V> cur)204     public static @NonNull <K, V> Map<K, V> emptyIfNull(@Nullable Map<K, V> cur) {
205         return cur == null ? Collections.emptyMap() : cur;
206     }
207 
208     /**
209      * Returns the size of the given collection, or 0 if null
210      */
size(@ullable Collection<?> cur)211     public static int size(@Nullable Collection<?> cur) {
212         return cur != null ? cur.size() : 0;
213     }
214 
215     /**
216      * Returns the size of the given map, or 0 if null
217      */
size(@ullable Map<?, ?> cur)218     public static int size(@Nullable Map<?, ?> cur) {
219         return cur != null ? cur.size() : 0;
220     }
221 
222     /**
223      * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
224      */
isEmpty(@ullable Collection<?> cur)225     public static boolean isEmpty(@Nullable Collection<?> cur) {
226         return size(cur) == 0;
227     }
228 
229     /**
230      * Returns the elements of the given list that are of type {@code c}
231      */
filter(@ullable List<?> list, Class<T> c)232     public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
233         if (isEmpty(list)) return Collections.emptyList();
234         ArrayList<T> result = null;
235         for (int i = 0; i < list.size(); i++) {
236             final Object item = list.get(i);
237             if (c.isInstance(item)) {
238                 result = ArrayUtils.add(result, (T) item);
239             }
240         }
241         return emptyIfNull(result);
242     }
243 
244     /**
245      * Returns whether there exists at least one element in the list for which
246      * condition {@code predicate} is true
247      */
any(@ullable List<T> items, java.util.function.Predicate<T> predicate)248     public static <T> boolean any(@Nullable List<T> items,
249             java.util.function.Predicate<T> predicate) {
250         return find(items, predicate) != null;
251     }
252 
253     /**
254      * Returns whether there exists at least one element in the set for which
255      * condition {@code predicate} is true
256      */
any(@ullable Set<T> items, java.util.function.Predicate<T> predicate)257     public static <T> boolean any(@Nullable Set<T> items,
258             java.util.function.Predicate<T> predicate) {
259         return find(items, predicate) != null;
260     }
261 
262     /**
263      * Returns the first element from the list for which
264      * condition {@code predicate} is true, or null if there is no such element
265      */
find(@ullable List<T> items, java.util.function.Predicate<T> predicate)266     public static @Nullable <T> T find(@Nullable List<T> items,
267             java.util.function.Predicate<T> predicate) {
268         if (isEmpty(items)) return null;
269         for (int i = 0; i < items.size(); i++) {
270             final T item = items.get(i);
271             if (predicate.test(item)) return item;
272         }
273         return null;
274     }
275 
276     /**
277      * Returns the first element from the set for which
278      * condition {@code predicate} is true, or null if there is no such element
279      */
find(@ullable Set<T> cur, java.util.function.Predicate<T> predicate)280     public static @Nullable <T> T find(@Nullable Set<T> cur,
281             java.util.function.Predicate<T> predicate) {
282         if (cur == null || predicate == null) return null;
283         int size = cur.size();
284         if (size == 0) return null;
285         try {
286             if (cur instanceof ArraySet) {
287                 ArraySet<T> arraySet = (ArraySet<T>) cur;
288                 for (int i = 0; i < size; i++) {
289                     T item = arraySet.valueAt(i);
290                     if (predicate.test(item)) {
291                         return item;
292                     }
293                 }
294             } else {
295                 for (T t : cur) {
296                     if (predicate.test(t)) {
297                         return t;
298                     }
299                 }
300             }
301         } catch (Exception e) {
302             throw ExceptionUtils.propagate(e);
303         }
304         return null;
305     }
306 
307     /**
308      * Similar to {@link List#add}, but with support for list values of {@code null} and
309      * {@link Collections#emptyList}
310      */
add(@ullable List<T> cur, T val)311     public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
312         if (cur == null || cur == Collections.emptyList()) {
313             cur = new ArrayList<>();
314         }
315         cur.add(val);
316         return cur;
317     }
318 
319     /**
320      * Similar to {@link List#add(int, Object)}, but with support for list values of {@code null}
321      * and {@link Collections#emptyList}
322      */
add(@ullable List<T> cur, int index, T val)323     public static @NonNull <T> List<T> add(@Nullable List<T> cur, int index, T val) {
324         if (cur == null || cur == Collections.emptyList()) {
325             cur = new ArrayList<>();
326         }
327         cur.add(index, val);
328         return cur;
329     }
330 
331     /**
332      * Similar to {@link Set#addAll(Collection)}}, but with support for list values of {@code null}
333      * and {@link Collections#emptySet}
334      */
addAll(@ullable Set<T> cur, @Nullable Collection<T> val)335     public static @NonNull <T> Set<T> addAll(@Nullable Set<T> cur, @Nullable Collection<T> val) {
336         if (isEmpty(val)) {
337             return cur != null ? cur : emptySet();
338         }
339         if (cur == null || cur == emptySet()) {
340             cur = new ArraySet<>();
341         }
342         cur.addAll(val);
343         return cur;
344     }
345 
346     /**
347      * @see #add(List, Object)
348      */
add(@ullable Set<T> cur, T val)349     public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
350         if (cur == null || cur == emptySet()) {
351             cur = new ArraySet<>();
352         }
353         cur.add(val);
354         return cur;
355     }
356 
357     /**
358      * @see #add(List, Object)
359      */
add(@ullable Map<K, V> map, K key, V value)360     public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
361         if (map == null || map == Collections.emptyMap()) {
362             map = new ArrayMap<>();
363         }
364         map.put(key, value);
365         return map;
366     }
367 
368     /**
369      * Similar to {@link List#remove}, but with support for list values of {@code null} and
370      * {@link Collections#emptyList}
371      */
remove(@ullable List<T> cur, T val)372     public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
373         if (isEmpty(cur)) {
374             return emptyIfNull(cur);
375         }
376         cur.remove(val);
377         return cur;
378     }
379 
380     /**
381      * @see #remove(List, Object)
382      */
remove(@ullable Set<T> cur, T val)383     public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
384         if (isEmpty(cur)) {
385             return emptyIfNull(cur);
386         }
387         cur.remove(val);
388         return cur;
389     }
390 
391     /**
392      * @return a list that will not be affected by mutations to the given original list.
393      */
copyOf(@ullable List<T> cur)394     public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
395         return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
396     }
397 
398     /**
399      * @return a list that will not be affected by mutations to the given original list.
400      */
copyOf(@ullable Set<T> cur)401     public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
402         return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
403     }
404 
405     /**
406      * @return a {@link Set} representing the given collection.
407      */
toSet(@ullable Collection<T> cur)408     public static @NonNull <T> Set<T> toSet(@Nullable Collection<T> cur) {
409         return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
410     }
411 
412     /**
413      * Applies {@code action} to each element in {@code cur}
414      *
415      * This avoids creating an iterator if the given set is an {@link ArraySet}
416      */
forEach(@ullable Set<T> cur, @Nullable ThrowingConsumer<T> action)417     public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
418         if (cur == null || action == null) return;
419         int size = cur.size();
420         if (size == 0) return;
421         try {
422             if (cur instanceof ArraySet) {
423                 ArraySet<T> arraySet = (ArraySet<T>) cur;
424                 for (int i = 0; i < size; i++) {
425                     action.acceptOrThrow(arraySet.valueAt(i));
426                 }
427             } else {
428                 for (T t : cur) {
429                     action.acceptOrThrow(t);
430                 }
431             }
432         } catch (Exception e) {
433             throw ExceptionUtils.propagate(e);
434         }
435     }
436 
437     /**
438      * Applies {@code action} to each element in {@code cur}
439      *
440      * This avoids creating an iterator if the given map is an {@link ArrayMap}
441      * For non-{@link ArrayMap}s it avoids creating {@link Map.Entry} instances
442      */
forEach(@ullable Map<K, V> cur, @Nullable BiConsumer<K, V> action)443     public static <K, V> void forEach(@Nullable Map<K, V> cur, @Nullable BiConsumer<K, V> action) {
444         if (cur == null || action == null) {
445             return;
446         }
447         int size = cur.size();
448         if (size == 0) {
449             return;
450         }
451 
452         if (cur instanceof ArrayMap) {
453             ArrayMap<K, V> arrayMap = (ArrayMap<K, V>) cur;
454             for (int i = 0; i < size; i++) {
455                 action.accept(arrayMap.keyAt(i), arrayMap.valueAt(i));
456             }
457         } else {
458             for (K key : cur.keySet()) {
459                 action.accept(key, cur.get(key));
460             }
461         }
462     }
463 
464     /**
465      * @return the first element if not empty/null, null otherwise
466      */
firstOrNull(@ullable List<T> cur)467     public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
468         return isEmpty(cur) ? null : cur.get(0);
469     }
470 
471     /**
472      * @return the first element if not empty/null, null otherwise
473      */
firstOrNull(@ullable Collection<T> cur)474     public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) {
475         return isEmpty(cur) ? null : cur.iterator().next();
476     }
477 
478     /**
479      * @return list of single given element if it's not null, empty list otherwise
480      */
singletonOrEmpty(@ullable T item)481     public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
482         return item == null ? Collections.emptyList() : Collections.singletonList(item);
483     }
484 }
485