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