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