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