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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.util.ArraySet;
22 import android.util.ExceptionUtils;
23 
24 import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
25 
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.function.Function;
33 import java.util.function.Predicate;
34 import java.util.stream.Stream;
35 
36 /**
37  * Utility methods for dealing with (typically {@code Nullable}) {@link Collection}s
38  *
39  * Unless a method specifies otherwise, a null value for a collection is treated as an empty
40  * collection of that type.
41  */
42 public class CollectionUtils {
CollectionUtils()43     private CollectionUtils() { /* cannot be instantiated */ }
44 
45     /**
46      * Returns a list of items from the provided list that match the given condition.
47      *
48      * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
49      * {@link Stream} instance
50      */
filter(@ullable List<T> list, java.util.function.Predicate<? super T> predicate)51     public static @NonNull <T> List<T> filter(@Nullable List<T> list,
52             java.util.function.Predicate<? super T> predicate) {
53         ArrayList<T> result = null;
54         for (int i = 0; i < size(list); i++) {
55             final T item = list.get(i);
56             if (predicate.test(item)) {
57                 result = ArrayUtils.add(result, item);
58             }
59         }
60         return emptyIfNull(result);
61     }
62 
63     /**
64      * @see #filter(List, java.util.function.Predicate)
65      */
filter(@ullable Set<T> set, java.util.function.Predicate<? super T> predicate)66     public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
67             java.util.function.Predicate<? super T> predicate) {
68         if (set == null || set.size() == 0) return Collections.emptySet();
69         ArraySet<T> result = null;
70         if (set instanceof ArraySet) {
71             ArraySet<T> arraySet = (ArraySet<T>) set;
72             int size = arraySet.size();
73             for (int i = 0; i < size; i++) {
74                 final T item = arraySet.valueAt(i);
75                 if (predicate.test(item)) {
76                     result = ArrayUtils.add(result, item);
77                 }
78             }
79         } else {
80             for (T item : set) {
81                 if (predicate.test(item)) {
82                     result = ArrayUtils.add(result, item);
83                 }
84             }
85         }
86         return emptyIfNull(result);
87     }
88 
89     /** 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)90     public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest,
91             @Nullable Predicate<? super T> predicate) {
92         for (int i = 0; i < size(source); i++) {
93             final T item = source.get(i);
94             if (predicate.test(item)) {
95                 dest.add(item);
96             }
97         }
98     }
99 
100     /**
101      * Returns a list of items resulting from applying the given function to each element of the
102      * provided list.
103      *
104      * The resulting list will have the same {@link #size} as the input one.
105      *
106      * This is similar to {@link Stream#map} but without the overhead of creating an intermediate
107      * {@link Stream} instance
108      */
map(@ullable List<I> cur, Function<? super I, ? extends O> f)109     public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
110             Function<? super I, ? extends O> f) {
111         if (isEmpty(cur)) return Collections.emptyList();
112         final ArrayList<O> result = new ArrayList<>();
113         for (int i = 0; i < cur.size(); i++) {
114             result.add(f.apply(cur.get(i)));
115         }
116         return result;
117     }
118 
119     /**
120      * @see #map(List, Function)
121      */
map(@ullable Set<I> cur, Function<? super I, ? extends O> f)122     public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
123             Function<? super I, ? extends O> f) {
124         if (isEmpty(cur)) return Collections.emptySet();
125         ArraySet<O> result = new ArraySet<>();
126         if (cur instanceof ArraySet) {
127             ArraySet<I> arraySet = (ArraySet<I>) cur;
128             int size = arraySet.size();
129             for (int i = 0; i < size; i++) {
130                 result.add(f.apply(arraySet.valueAt(i)));
131             }
132         } else {
133             for (I item : cur) {
134                 result.add(f.apply(item));
135             }
136         }
137         return result;
138     }
139 
140     /**
141      * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
142      *
143      * Calling this is equivalent (but more memory efficient) to:
144      *
145      * {@code
146      *      filter(
147      *          map(cur, f),
148      *          i -> { i != null })
149      * }
150      */
mapNotNull(@ullable List<I> cur, Function<? super I, ? extends O> f)151     public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
152             Function<? super I, ? extends O> f) {
153         if (isEmpty(cur)) return Collections.emptyList();
154         List<O> result = null;
155         for (int i = 0; i < cur.size(); i++) {
156             O transformed = f.apply(cur.get(i));
157             if (transformed != null) {
158                 result = add(result, transformed);
159             }
160         }
161         return emptyIfNull(result);
162     }
163 
164     /**
165      * Returns the given list, or an immutable empty list if the provided list is null
166      *
167      * This can be used to guarantee null-safety without paying the price of extra allocations
168      *
169      * @see Collections#emptyList
170      */
emptyIfNull(@ullable List<T> cur)171     public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
172         return cur == null ? Collections.emptyList() : cur;
173     }
174 
175     /**
176      * Returns the given set, or an immutable empty set if the provided set is null
177      *
178      * This can be used to guarantee null-safety without paying the price of extra allocations
179      *
180      * @see Collections#emptySet
181      */
emptyIfNull(@ullable Set<T> cur)182     public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) {
183         return cur == null ? Collections.emptySet() : cur;
184     }
185 
186     /**
187      * Returns the size of the given collection, or 0 if null
188      */
size(@ullable Collection<?> cur)189     public static int size(@Nullable Collection<?> cur) {
190         return cur != null ? cur.size() : 0;
191     }
192 
193     /**
194      * Returns the size of the given map, or 0 if null
195      */
size(@ullable Map<?, ?> cur)196     public static int size(@Nullable Map<?, ?> cur) {
197         return cur != null ? cur.size() : 0;
198     }
199 
200     /**
201      * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
202      */
isEmpty(@ullable Collection<?> cur)203     public static boolean isEmpty(@Nullable Collection<?> cur) {
204         return size(cur) == 0;
205     }
206 
207     /**
208      * Returns the elements of the given list that are of type {@code c}
209      */
filter(@ullable List<?> list, Class<T> c)210     public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
211         if (isEmpty(list)) return Collections.emptyList();
212         ArrayList<T> result = null;
213         for (int i = 0; i < list.size(); i++) {
214             final Object item = list.get(i);
215             if (c.isInstance(item)) {
216                 result = ArrayUtils.add(result, (T) item);
217             }
218         }
219         return emptyIfNull(result);
220     }
221 
222     /**
223      * Returns whether there exists at least one element in the list for which
224      * condition {@code predicate} is true
225      */
any(@ullable List<T> items, java.util.function.Predicate<T> predicate)226     public static <T> boolean any(@Nullable List<T> items,
227             java.util.function.Predicate<T> predicate) {
228         return find(items, predicate) != null;
229     }
230 
231     /**
232      * Returns the first element from the list for which
233      * condition {@code predicate} is true, or null if there is no such element
234      */
find(@ullable List<T> items, java.util.function.Predicate<T> predicate)235     public static @Nullable <T> T find(@Nullable List<T> items,
236             java.util.function.Predicate<T> predicate) {
237         if (isEmpty(items)) return null;
238         for (int i = 0; i < items.size(); i++) {
239             final T item = items.get(i);
240             if (predicate.test(item)) return item;
241         }
242         return null;
243     }
244 
245     /**
246      * Similar to {@link List#add}, but with support for list values of {@code null} and
247      * {@link Collections#emptyList}
248      */
add(@ullable List<T> cur, T val)249     public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
250         if (cur == null || cur == Collections.emptyList()) {
251             cur = new ArrayList<>();
252         }
253         cur.add(val);
254         return cur;
255     }
256 
257     /**
258      * @see #add(List, Object)
259      */
add(@ullable Set<T> cur, T val)260     public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
261         if (cur == null || cur == Collections.emptySet()) {
262             cur = new ArraySet<>();
263         }
264         cur.add(val);
265         return cur;
266     }
267 
268     /**
269      * Similar to {@link List#remove}, but with support for list values of {@code null} and
270      * {@link Collections#emptyList}
271      */
remove(@ullable List<T> cur, T val)272     public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
273         if (isEmpty(cur)) {
274             return emptyIfNull(cur);
275         }
276         cur.remove(val);
277         return cur;
278     }
279 
280     /**
281      * @see #remove(List, Object)
282      */
remove(@ullable Set<T> cur, T val)283     public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
284         if (isEmpty(cur)) {
285             return emptyIfNull(cur);
286         }
287         cur.remove(val);
288         return cur;
289     }
290 
291     /**
292      * @return a list that will not be affected by mutations to the given original list.
293      */
copyOf(@ullable List<T> cur)294     public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
295         return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
296     }
297 
298     /**
299      * @return a list that will not be affected by mutations to the given original list.
300      */
copyOf(@ullable Set<T> cur)301     public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
302         return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur);
303     }
304 
305     /**
306      * Applies {@code action} to each element in {@code cur}
307      *
308      * This avoids creating an iterator if the given set is an {@link ArraySet}
309      */
forEach(@ullable Set<T> cur, @Nullable ThrowingConsumer<T> action)310     public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
311         if (cur == null || action == null) return;
312         int size = cur.size();
313         if (size == 0) return;
314         try {
315             if (cur instanceof ArraySet) {
316                 ArraySet<T> arraySet = (ArraySet<T>) cur;
317                 for (int i = 0; i < size; i++) {
318                     action.acceptOrThrow(arraySet.valueAt(i));
319                 }
320             } else {
321                 for (T t : cur) {
322                     action.acceptOrThrow(t);
323                 }
324             }
325         } catch (Exception e) {
326             throw ExceptionUtils.propagate(e);
327         }
328     }
329 
330     /**
331      * @return the first element if not empty/null, null otherwise
332      */
firstOrNull(@ullable List<T> cur)333     public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
334         return isEmpty(cur) ? null : cur.get(0);
335     }
336 
337     /**
338      * @return the first element if not empty/null, null otherwise
339      */
firstOrNull(@ullable Collection<T> cur)340     public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) {
341         return isEmpty(cur) ? null : cur.iterator().next();
342     }
343 
344     /**
345      * @return list of single given element if it's not null, empty list otherwise
346      */
singletonOrEmpty(@ullable T item)347     public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
348         return item == null ? Collections.emptyList() : Collections.singletonList(item);
349     }
350 }
351