blob: f9cf23b29a696fe2ca5f5956ca54a0841f20359a [file] [log] [blame]
Eugene Susla6a7006a2017-03-13 12:57:58 -07001/*
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
17package com.android.internal.util;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
Eugene Susla53f4df32019-05-17 13:23:44 -070021import android.util.ArrayMap;
Eugene Susla554edd32017-05-24 16:49:59 -070022import android.util.ArraySet;
Eugene Susla612311e2017-07-06 11:12:52 -070023import android.util.ExceptionUtils;
24
25import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
Eugene Susla6a7006a2017-03-13 12:57:58 -070026
27import java.util.ArrayList;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.List;
Eugene Suslaabdefba2018-11-09 18:06:43 -080031import java.util.Map;
Eugene Susla0c4a9262017-06-09 18:03:14 -070032import java.util.Set;
Eugene Susla53f4df32019-05-17 13:23:44 -070033import java.util.function.BiConsumer;
Eugene Susla2f5ee712017-06-23 17:25:24 -070034import java.util.function.Function;
Makoto Onuki0b575a32018-04-16 14:33:59 -070035import java.util.function.Predicate;
Eugene Susla6a7006a2017-03-13 12:57:58 -070036import java.util.stream.Stream;
37
38/**
Jeff Sharkey67f9d502017-08-05 13:49:13 -060039 * Utility methods for dealing with (typically {@code Nullable}) {@link Collection}s
Eugene Susla6a7006a2017-03-13 12:57:58 -070040 *
41 * Unless a method specifies otherwise, a null value for a collection is treated as an empty
42 * collection of that type.
43 */
44public class CollectionUtils {
45 private CollectionUtils() { /* cannot be instantiated */ }
46
47 /**
48 * Returns a list of items from the provided list that match the given condition.
49 *
50 * This is similar to {@link Stream#filter} but without the overhead of creating an intermediate
51 * {@link Stream} instance
52 */
53 public static @NonNull <T> List<T> filter(@Nullable List<T> list,
54 java.util.function.Predicate<? super T> predicate) {
55 ArrayList<T> result = null;
56 for (int i = 0; i < size(list); i++) {
57 final T item = list.get(i);
58 if (predicate.test(item)) {
59 result = ArrayUtils.add(result, item);
60 }
61 }
62 return emptyIfNull(result);
63 }
64
65 /**
Eugene Susla612311e2017-07-06 11:12:52 -070066 * @see #filter(List, java.util.function.Predicate)
67 */
68 public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
69 java.util.function.Predicate<? super T> predicate) {
70 if (set == null || set.size() == 0) return Collections.emptySet();
71 ArraySet<T> result = null;
72 if (set instanceof ArraySet) {
73 ArraySet<T> arraySet = (ArraySet<T>) set;
74 int size = arraySet.size();
75 for (int i = 0; i < size; i++) {
76 final T item = arraySet.valueAt(i);
77 if (predicate.test(item)) {
78 result = ArrayUtils.add(result, item);
79 }
80 }
81 } else {
82 for (T item : set) {
83 if (predicate.test(item)) {
84 result = ArrayUtils.add(result, item);
85 }
86 }
87 }
88 return emptyIfNull(result);
89 }
90
Makoto Onuki0b575a32018-04-16 14:33:59 -070091 /** Add all elements matching {@code predicate} in {@code source} to {@code dest}. */
92 public static <T> void addIf(@Nullable List<T> source, @NonNull Collection<? super T> dest,
93 @Nullable Predicate<? super T> predicate) {
94 for (int i = 0; i < size(source); i++) {
95 final T item = source.get(i);
96 if (predicate.test(item)) {
97 dest.add(item);
98 }
99 }
100 }
101
Eugene Susla612311e2017-07-06 11:12:52 -0700102 /**
Eugene Susla6a7006a2017-03-13 12:57:58 -0700103 * Returns a list of items resulting from applying the given function to each element of the
104 * provided list.
105 *
106 * The resulting list will have the same {@link #size} as the input one.
107 *
108 * This is similar to {@link Stream#map} but without the overhead of creating an intermediate
109 * {@link Stream} instance
110 */
111 public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
112 Function<? super I, ? extends O> f) {
Eugene Suslacf00ade2017-04-10 11:51:58 -0700113 if (isEmpty(cur)) return Collections.emptyList();
Eugene Susla6a7006a2017-03-13 12:57:58 -0700114 final ArrayList<O> result = new ArrayList<>();
115 for (int i = 0; i < cur.size(); i++) {
116 result.add(f.apply(cur.get(i)));
117 }
118 return result;
119 }
120
121 /**
Eugene Susla612311e2017-07-06 11:12:52 -0700122 * @see #map(List, Function)
123 */
124 public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
125 Function<? super I, ? extends O> f) {
126 if (isEmpty(cur)) return Collections.emptySet();
127 ArraySet<O> result = new ArraySet<>();
128 if (cur instanceof ArraySet) {
129 ArraySet<I> arraySet = (ArraySet<I>) cur;
130 int size = arraySet.size();
131 for (int i = 0; i < size; i++) {
132 result.add(f.apply(arraySet.valueAt(i)));
133 }
134 } else {
135 for (I item : cur) {
136 result.add(f.apply(item));
137 }
138 }
139 return result;
140 }
141
142 /**
Eugene Suslacf00ade2017-04-10 11:51:58 -0700143 * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
144 *
145 * Calling this is equivalent (but more memory efficient) to:
146 *
147 * {@code
148 * filter(
149 * map(cur, f),
150 * i -> { i != null })
151 * }
152 */
153 public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
154 Function<? super I, ? extends O> f) {
155 if (isEmpty(cur)) return Collections.emptyList();
Eugene Susla72b56162017-12-06 13:08:39 -0800156 List<O> result = null;
Eugene Suslacf00ade2017-04-10 11:51:58 -0700157 for (int i = 0; i < cur.size(); i++) {
158 O transformed = f.apply(cur.get(i));
159 if (transformed != null) {
Eugene Susla72b56162017-12-06 13:08:39 -0800160 result = add(result, transformed);
Eugene Suslacf00ade2017-04-10 11:51:58 -0700161 }
162 }
Eugene Susla72b56162017-12-06 13:08:39 -0800163 return emptyIfNull(result);
Eugene Suslacf00ade2017-04-10 11:51:58 -0700164 }
165
166 /**
Eugene Susla6a7006a2017-03-13 12:57:58 -0700167 * Returns the given list, or an immutable empty list if the provided list is null
168 *
Eugene Susla0c4a9262017-06-09 18:03:14 -0700169 * This can be used to guarantee null-safety without paying the price of extra allocations
Eugene Susla6a7006a2017-03-13 12:57:58 -0700170 *
171 * @see Collections#emptyList
172 */
173 public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
174 return cur == null ? Collections.emptyList() : cur;
175 }
176
177 /**
Eugene Susla0c4a9262017-06-09 18:03:14 -0700178 * Returns the given set, or an immutable empty set if the provided set is null
179 *
180 * This can be used to guarantee null-safety without paying the price of extra allocations
181 *
182 * @see Collections#emptySet
183 */
184 public static @NonNull <T> Set<T> emptyIfNull(@Nullable Set<T> cur) {
185 return cur == null ? Collections.emptySet() : cur;
186 }
187
188 /**
Eugene Susla1abf48e2018-01-02 15:25:12 -0800189 * Returns the size of the given collection, or 0 if null
Eugene Susla6a7006a2017-03-13 12:57:58 -0700190 */
191 public static int size(@Nullable Collection<?> cur) {
192 return cur != null ? cur.size() : 0;
193 }
194
195 /**
Eugene Suslaabdefba2018-11-09 18:06:43 -0800196 * Returns the size of the given map, or 0 if null
197 */
198 public static int size(@Nullable Map<?, ?> cur) {
199 return cur != null ? cur.size() : 0;
200 }
201
202 /**
Eugene Susla1abf48e2018-01-02 15:25:12 -0800203 * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
204 */
205 public static boolean isEmpty(@Nullable Collection<?> cur) {
206 return size(cur) == 0;
207 }
208
209 /**
Eugene Susla6a7006a2017-03-13 12:57:58 -0700210 * Returns the elements of the given list that are of type {@code c}
211 */
212 public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
Eugene Suslacf00ade2017-04-10 11:51:58 -0700213 if (isEmpty(list)) return Collections.emptyList();
Eugene Susla6a7006a2017-03-13 12:57:58 -0700214 ArrayList<T> result = null;
215 for (int i = 0; i < list.size(); i++) {
216 final Object item = list.get(i);
217 if (c.isInstance(item)) {
218 result = ArrayUtils.add(result, (T) item);
219 }
220 }
221 return emptyIfNull(result);
222 }
223
224 /**
225 * Returns whether there exists at least one element in the list for which
226 * condition {@code predicate} is true
227 */
228 public static <T> boolean any(@Nullable List<T> items,
229 java.util.function.Predicate<T> predicate) {
230 return find(items, predicate) != null;
231 }
232
233 /**
234 * Returns the first element from the list for which
235 * condition {@code predicate} is true, or null if there is no such element
236 */
237 public static @Nullable <T> T find(@Nullable List<T> items,
238 java.util.function.Predicate<T> predicate) {
Eugene Suslacf00ade2017-04-10 11:51:58 -0700239 if (isEmpty(items)) return null;
Eugene Susla6a7006a2017-03-13 12:57:58 -0700240 for (int i = 0; i < items.size(); i++) {
241 final T item = items.get(i);
242 if (predicate.test(item)) return item;
243 }
244 return null;
245 }
Eugene Susla4df89bc2017-03-28 20:27:28 -0700246
247 /**
248 * Similar to {@link List#add}, but with support for list values of {@code null} and
249 * {@link Collections#emptyList}
250 */
251 public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
252 if (cur == null || cur == Collections.emptyList()) {
253 cur = new ArrayList<>();
254 }
255 cur.add(val);
256 return cur;
257 }
258
259 /**
Eugene Susla612311e2017-07-06 11:12:52 -0700260 * @see #add(List, Object)
261 */
262 public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
263 if (cur == null || cur == Collections.emptySet()) {
264 cur = new ArraySet<>();
265 }
266 cur.add(val);
267 return cur;
268 }
269
270 /**
Eugene Susla4df89bc2017-03-28 20:27:28 -0700271 * Similar to {@link List#remove}, but with support for list values of {@code null} and
272 * {@link Collections#emptyList}
273 */
274 public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
Eugene Suslacf00ade2017-04-10 11:51:58 -0700275 if (isEmpty(cur)) {
276 return emptyIfNull(cur);
Eugene Susla4df89bc2017-03-28 20:27:28 -0700277 }
278 cur.remove(val);
279 return cur;
280 }
281
Eugene Suslacf00ade2017-04-10 11:51:58 -0700282 /**
Eugene Susla612311e2017-07-06 11:12:52 -0700283 * @see #remove(List, Object)
284 */
285 public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
286 if (isEmpty(cur)) {
287 return emptyIfNull(cur);
288 }
289 cur.remove(val);
290 return cur;
291 }
292
293 /**
Eugene Suslacf00ade2017-04-10 11:51:58 -0700294 * @return a list that will not be affected by mutations to the given original list.
295 */
296 public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
297 return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
298 }
Eugene Susla612311e2017-07-06 11:12:52 -0700299
300 /**
301 * @return a list that will not be affected by mutations to the given original list.
302 */
303 public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
304 return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur);
305 }
306
307 /**
308 * Applies {@code action} to each element in {@code cur}
309 *
310 * This avoids creating an iterator if the given set is an {@link ArraySet}
311 */
312 public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
313 if (cur == null || action == null) return;
314 int size = cur.size();
315 if (size == 0) return;
316 try {
317 if (cur instanceof ArraySet) {
318 ArraySet<T> arraySet = (ArraySet<T>) cur;
319 for (int i = 0; i < size; i++) {
Eugene Susla4b7c919e2017-12-07 11:23:50 -0800320 action.acceptOrThrow(arraySet.valueAt(i));
Eugene Susla612311e2017-07-06 11:12:52 -0700321 }
322 } else {
323 for (T t : cur) {
Eugene Susla4b7c919e2017-12-07 11:23:50 -0800324 action.acceptOrThrow(t);
Eugene Susla612311e2017-07-06 11:12:52 -0700325 }
326 }
327 } catch (Exception e) {
328 throw ExceptionUtils.propagate(e);
329 }
330 }
Jeff Sharkey11697f52018-12-13 10:14:42 -0700331
Eugene Susla4ab95112018-12-17 14:45:11 -0800332 /**
Eugene Susla53f4df32019-05-17 13:23:44 -0700333 * Applies {@code action} to each element in {@code cur}
334 *
335 * This avoids creating an iterator if the given map is an {@link ArrayMap}
336 * For non-{@link ArrayMap}s it avoids creating {@link Map.Entry} instances
337 */
338 public static <K, V> void forEach(@Nullable Map<K, V> cur, @Nullable BiConsumer<K, V> action) {
339 if (cur == null || action == null) {
340 return;
341 }
342 int size = cur.size();
343 if (size == 0) {
344 return;
345 }
346
347 if (cur instanceof ArrayMap) {
348 ArrayMap<K, V> arrayMap = (ArrayMap<K, V>) cur;
349 for (int i = 0; i < size; i++) {
350 action.accept(arrayMap.keyAt(i), arrayMap.valueAt(i));
351 }
352 } else {
353 for (K key : cur.keySet()) {
354 action.accept(key, cur.get(key));
355 }
356 }
357 }
358
359 /**
Eugene Susla4ab95112018-12-17 14:45:11 -0800360 * @return the first element if not empty/null, null otherwise
361 */
362 public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) {
363 return isEmpty(cur) ? null : cur.get(0);
364 }
365
366 /**
Hai Zhang85fd0622019-02-01 14:06:04 -0800367 * @return the first element if not empty/null, null otherwise
368 */
369 public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) {
370 return isEmpty(cur) ? null : cur.iterator().next();
371 }
372
373 /**
Eugene Susla4ab95112018-12-17 14:45:11 -0800374 * @return list of single given element if it's not null, empty list otherwise
375 */
376 public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) {
377 return item == null ? Collections.emptyList() : Collections.singletonList(item);
378 }
Eugene Susla6a7006a2017-03-13 12:57:58 -0700379}