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