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 whether the given map {@link Map#isEmpty is empty} or {@code null}
231      */
isEmpty(@ullable Map<?, ?> cur)232     public static boolean isEmpty(@Nullable Map<?, ?> cur) {
233         return size(cur) == 0;
234     }
235 
236     /**
237      * Returns the elements of the given list that are of type {@code c}
238      */
filter(@ullable List<?> list, Class<T> c)239     public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
240         if (isEmpty(list)) return Collections.emptyList();
241         ArrayList<T> result = null;
242         for (int i = 0; i < list.size(); i++) {
243             final Object item = list.get(i);
244             if (c.isInstance(item)) {
245                 result = ArrayUtils.add(result, (T) item);
246             }
247         }
248         return emptyIfNull(result);
249     }
250 
251     /**
252      * Returns whether there exists at least one element in the list for which
253      * condition {@code predicate} is true
254      */
any(@ullable List<T> items, java.util.function.Predicate<T> predicate)255     public static <T> boolean any(@Nullable List<T> items,
256             java.util.function.Predicate<T> predicate) {
257         return find(items, predicate) != null;
258     }
259 
260     /**
261      * Returns whether there exists at least one element in the set for which
262      * condition {@code predicate} is true
263      */
any(@ullable Set<T> items, java.util.function.Predicate<T> predicate)264     public static <T> boolean any(@Nullable Set<T> items,
265             java.util.function.Predicate<T> predicate) {
266         return find(items, predicate) != null;
267     }
268 
269     /**
270      * Returns the first element from the list for which
271      * condition {@code predicate} is true, or null if there is no such element
272      */
find(@ullable List<T> items, java.util.function.Predicate<T> predicate)273     public static @Nullable <T> T find(@Nullable List<T> items,
274             java.util.function.Predicate<T> predicate) {
275         if (isEmpty(items)) return null;
276         for (int i = 0; i < items.size(); i++) {
277             final T item = items.get(i);
278             if (predicate.test(item)) return item;
279         }
280         return null;
281     }
282 
283     /**
284      * Returns the first element from the set for which
285      * condition {@code predicate} is true, or null if there is no such element
286      */
find(@ullable Set<T> cur, java.util.function.Predicate<T> predicate)287     public static @Nullable <T> T find(@Nullable Set<T> cur,
288             java.util.function.Predicate<T> predicate) {
289         if (cur == null || predicate == null) return null;
290         int size = cur.size();
291         if (size == 0) return null;
292         try {
293             if (cur instanceof ArraySet) {
294                 ArraySet<T> arraySet = (ArraySet<T>) cur;
295                 for (int i = 0; i < size; i++) {
296                     T item = arraySet.valueAt(i);
297                     if (predicate.test(item)) {
298                         return item;
299                     }
300                 }
301             } else {
302                 for (T t : cur) {
303                     if (predicate.test(t)) {
304                         return t;
305                     }
306                 }
307             }
308         } catch (Exception e) {
309             throw ExceptionUtils.propagate(e);
310         }
311         return null;
312     }
313 
314     /**
315      * Similar to {@link List#add}, but with support for list values of {@code null} and
316      * {@link Collections#emptyList}
317      */
add(@ullable List<T> cur, T val)318     public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
319         if (cur == null || cur == Collections.emptyList()) {
320             cur = new ArrayList<>();
321         }
322         cur.add(val);
323         return cur;
324     }
325 
326     /**
327      * Similar to {@link List#add(int, Object)}, but with support for list values of {@code null}
328      * and {@link Collections#emptyList}
329      */
add(@ullable List<T> cur, int index, T val)330     public static @NonNull <T> List<T> add(@Nullable List<T> cur, int index, T val) {
331         if (cur == null || cur == Collections.emptyList()) {
332             cur = new ArrayList<>();
333         }
334         cur.add(index, val);
335         return cur;
336     }
337 
338     /**
339      * Similar to {@link Set#addAll(Collection)}}, but with support for list values of {@code null}
340      * and {@link Collections#emptySet}
341      */
addAll(@ullable Set<T> cur, @Nullable Collection<T> val)342     public static @NonNull <T> Set<T> addAll(@Nullable Set<T> cur, @Nullable Collection<T> val) {
343         if (isEmpty(val)) {
344             return cur != null ? cur : emptySet();
345         }
346         if (cur == null || cur == emptySet()) {
347             cur = new ArraySet<>();
348         }
349         cur.addAll(val);
350         return cur;
351     }
352 
353     /**
354      * @see #add(List, Object)
355      */
add(@ullable Set<T> cur, T val)356     public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
357         if (cur == null || cur == emptySet()) {
358             cur = new ArraySet<>();
359         }
360         cur.add(val);
361         return cur;
362     }
363 
364     /**
365      * @see #add(List, Object)
366      */
add(@ullable Map<K, V> map, K key, V value)367     public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
368         if (map == null || map == Collections.emptyMap()) {
369             map = new ArrayMap<>();
370         }
371         map.put(key, value);
372         return map;
373     }
374 
375     /**
376      * Similar to {@link List#remove}, but with support for list values of {@code null} and
377      * {@link Collections#emptyList}
378      */
remove(@ullable List<T> cur, T val)379     public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
380         if (isEmpty(cur)) {
381             return emptyIfNull(cur);
382         }
383         cur.remove(val);
384         return cur;
385     }
386 
387     /**
388      * @see #remove(List, Object)
389      */
remove(@ullable Set<T> cur, T val)390     public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
391         if (isEmpty(cur)) {
392             return emptyIfNull(cur);
393         }
394         cur.remove(val);
395         return cur;
396     }
397 
398     /**
399      * @return a list that will not be affected by mutations to the given original list.
400      */
copyOf(@ullable List<T> cur)401     public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
402         return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
403     }
404 
405     /**
406      * @return a list that will not be affected by mutations to the given original list.
407      */
copyOf(@ullable Set<T> cur)408     public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
409         return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
410     }
411 
412     /**
413      * @return a {@link Set} representing the given collection.
414      */
toSet(@ullable Collection<T> cur)415     public static @NonNull <T> Set<T> toSet(@Nullable Collection<T> cur) {
416         return isEmpty(cur) ? emptySet() : new ArraySet<>(cur);
417     }
418 
419     /**
420      * Applies {@code action} to each element in {@code cur}
421      *
422      * This avoids creating an iterator if the given set is an {@link ArraySet}
423      */
forEach(@ullable Set<T> cur, @Nullable ThrowingConsumer<T> action)424     public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
425         if (cur == null || action == null) return;
426         int size = cur.size();
427         if (size == 0) return;
428         try {
429             if (cur instanceof ArraySet) {
430                 ArraySet<T> arraySet = (ArraySet<T>) cur;
431                 for (int i = 0; i < size; i++) {
432                     action.acceptOrThrow(arraySet.valueAt(i));
433                 }
434             } else {
435                 for (T t : cur) {
436                     action.acceptOrThrow(t);
437                 }
438             }
439         } catch (Exception e) {
440             throw ExceptionUtils.propagate(e);
441         }
442     }
443 
444     /**
445      * Applies {@code action} to each element in {@code cur}
446      *
447      * This avoids creating an iterator if the given map is an {@link ArrayMap}
448      * For non-{@link ArrayMap}s it avoids creating {@link Map.Entry} instances
449      */
forEach(@ullable Map<K, V> cur, @Nullable BiConsumer<K, V> action)450     public static <K, V> void forEach(@Nullable Map<K, V> cur, @Nullable BiConsumer<K, V> action) {
451         if (cur == null || action == null) {
452             return;
453         }
454         int size = cur.size();
455         if (size == 0) {
456             return;
457         }
458 
459         if (cur instanceof ArrayMap) {
460             ArrayMap<K, V> arrayMap = (ArrayMap<K, V>) cur;
461             for (int i = 0; i < size; i++) {
462                 action.accept(arrayMap.keyAt(i), arrayMap.valueAt(i));
463             }
464         } else {
465             for (K key : cur.keySet()) {
466                 action.accept(key, cur.get(key));
467             }
468         }
469     }
470 
471     /**
472      * @return the first element if not empty/null, null otherwise
473      */
firstOrNull(@ullable List<T> cur)474     public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
475         return isEmpty(cur) ? null : cur.get(0);
476     }
477 
478     /**
479      * @return the first element if not empty/null, null otherwise
480      */
firstOrNull(@ullable Collection<T> cur)481     public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) {
482         return isEmpty(cur) ? null : cur.iterator().next();
483     }
484 
485     /**
486      * @return list of single given element if it's not null, empty list otherwise
487      */
singletonOrEmpty(@ullable T item)488     public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
489         return item == null ? Collections.emptyList() : Collections.singletonList(item);
490     }
491 }
492