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 whether the given map {@link Map#isEmpty is empty} or {@code null} 231 */ isEmpty(@ullable Map<?, ?> cur)232 public static boolean isEmpty(@Nullable Map<?, ?> cur) { 233 return size(cur) == 0; 234 } 235 236 /** 237 * Returns the elements of the given list that are of type {@code c} 238 */ filter(@ullable List<?> list, Class<T> c)239 public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) { 240 if (isEmpty(list)) return Collections.emptyList(); 241 ArrayList<T> result = null; 242 for (int i = 0; i < list.size(); i++) { 243 final Object item = list.get(i); 244 if (c.isInstance(item)) { 245 result = ArrayUtils.add(result, (T) item); 246 } 247 } 248 return emptyIfNull(result); 249 } 250 251 /** 252 * Returns whether there exists at least one element in the list for which 253 * condition {@code predicate} is true 254 */ any(@ullable List<T> items, java.util.function.Predicate<T> predicate)255 public static <T> boolean any(@Nullable List<T> items, 256 java.util.function.Predicate<T> predicate) { 257 return find(items, predicate) != null; 258 } 259 260 /** 261 * Returns whether there exists at least one element in the set for which 262 * condition {@code predicate} is true 263 */ any(@ullable Set<T> items, java.util.function.Predicate<T> predicate)264 public static <T> boolean any(@Nullable Set<T> items, 265 java.util.function.Predicate<T> predicate) { 266 return find(items, predicate) != null; 267 } 268 269 /** 270 * Returns the first element from the list for which 271 * condition {@code predicate} is true, or null if there is no such element 272 */ find(@ullable List<T> items, java.util.function.Predicate<T> predicate)273 public static @Nullable <T> T find(@Nullable List<T> items, 274 java.util.function.Predicate<T> predicate) { 275 if (isEmpty(items)) return null; 276 for (int i = 0; i < items.size(); i++) { 277 final T item = items.get(i); 278 if (predicate.test(item)) return item; 279 } 280 return null; 281 } 282 283 /** 284 * Returns the first element from the set for which 285 * condition {@code predicate} is true, or null if there is no such element 286 */ find(@ullable Set<T> cur, java.util.function.Predicate<T> predicate)287 public static @Nullable <T> T find(@Nullable Set<T> cur, 288 java.util.function.Predicate<T> predicate) { 289 if (cur == null || predicate == null) return null; 290 int size = cur.size(); 291 if (size == 0) return null; 292 try { 293 if (cur instanceof ArraySet) { 294 ArraySet<T> arraySet = (ArraySet<T>) cur; 295 for (int i = 0; i < size; i++) { 296 T item = arraySet.valueAt(i); 297 if (predicate.test(item)) { 298 return item; 299 } 300 } 301 } else { 302 for (T t : cur) { 303 if (predicate.test(t)) { 304 return t; 305 } 306 } 307 } 308 } catch (Exception e) { 309 throw ExceptionUtils.propagate(e); 310 } 311 return null; 312 } 313 314 /** 315 * Similar to {@link List#add}, but with support for list values of {@code null} and 316 * {@link Collections#emptyList} 317 */ add(@ullable List<T> cur, T val)318 public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) { 319 if (cur == null || cur == Collections.emptyList()) { 320 cur = new ArrayList<>(); 321 } 322 cur.add(val); 323 return cur; 324 } 325 326 /** 327 * Similar to {@link List#add(int, Object)}, but with support for list values of {@code null} 328 * and {@link Collections#emptyList} 329 */ add(@ullable List<T> cur, int index, T val)330 public static @NonNull <T> List<T> add(@Nullable List<T> cur, int index, T val) { 331 if (cur == null || cur == Collections.emptyList()) { 332 cur = new ArrayList<>(); 333 } 334 cur.add(index, val); 335 return cur; 336 } 337 338 /** 339 * Similar to {@link Set#addAll(Collection)}}, but with support for list values of {@code null} 340 * and {@link Collections#emptySet} 341 */ addAll(@ullable Set<T> cur, @Nullable Collection<T> val)342 public static @NonNull <T> Set<T> addAll(@Nullable Set<T> cur, @Nullable Collection<T> val) { 343 if (isEmpty(val)) { 344 return cur != null ? cur : emptySet(); 345 } 346 if (cur == null || cur == emptySet()) { 347 cur = new ArraySet<>(); 348 } 349 cur.addAll(val); 350 return cur; 351 } 352 353 /** 354 * @see #add(List, Object) 355 */ add(@ullable Set<T> cur, T val)356 public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) { 357 if (cur == null || cur == emptySet()) { 358 cur = new ArraySet<>(); 359 } 360 cur.add(val); 361 return cur; 362 } 363 364 /** 365 * @see #add(List, Object) 366 */ add(@ullable Map<K, V> map, K key, V value)367 public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) { 368 if (map == null || map == Collections.emptyMap()) { 369 map = new ArrayMap<>(); 370 } 371 map.put(key, value); 372 return map; 373 } 374 375 /** 376 * Similar to {@link List#remove}, but with support for list values of {@code null} and 377 * {@link Collections#emptyList} 378 */ remove(@ullable List<T> cur, T val)379 public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) { 380 if (isEmpty(cur)) { 381 return emptyIfNull(cur); 382 } 383 cur.remove(val); 384 return cur; 385 } 386 387 /** 388 * @see #remove(List, Object) 389 */ remove(@ullable Set<T> cur, T val)390 public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) { 391 if (isEmpty(cur)) { 392 return emptyIfNull(cur); 393 } 394 cur.remove(val); 395 return cur; 396 } 397 398 /** 399 * @return a list that will not be affected by mutations to the given original list. 400 */ copyOf(@ullable List<T> cur)401 public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) { 402 return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur); 403 } 404 405 /** 406 * @return a list that will not be affected by mutations to the given original list. 407 */ copyOf(@ullable Set<T> cur)408 public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) { 409 return isEmpty(cur) ? emptySet() : new ArraySet<>(cur); 410 } 411 412 /** 413 * @return a {@link Set} representing the given collection. 414 */ toSet(@ullable Collection<T> cur)415 public static @NonNull <T> Set<T> toSet(@Nullable Collection<T> cur) { 416 return isEmpty(cur) ? emptySet() : new ArraySet<>(cur); 417 } 418 419 /** 420 * Applies {@code action} to each element in {@code cur} 421 * 422 * This avoids creating an iterator if the given set is an {@link ArraySet} 423 */ forEach(@ullable Set<T> cur, @Nullable ThrowingConsumer<T> action)424 public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) { 425 if (cur == null || action == null) return; 426 int size = cur.size(); 427 if (size == 0) return; 428 try { 429 if (cur instanceof ArraySet) { 430 ArraySet<T> arraySet = (ArraySet<T>) cur; 431 for (int i = 0; i < size; i++) { 432 action.acceptOrThrow(arraySet.valueAt(i)); 433 } 434 } else { 435 for (T t : cur) { 436 action.acceptOrThrow(t); 437 } 438 } 439 } catch (Exception e) { 440 throw ExceptionUtils.propagate(e); 441 } 442 } 443 444 /** 445 * Applies {@code action} to each element in {@code cur} 446 * 447 * This avoids creating an iterator if the given map is an {@link ArrayMap} 448 * For non-{@link ArrayMap}s it avoids creating {@link Map.Entry} instances 449 */ forEach(@ullable Map<K, V> cur, @Nullable BiConsumer<K, V> action)450 public static <K, V> void forEach(@Nullable Map<K, V> cur, @Nullable BiConsumer<K, V> action) { 451 if (cur == null || action == null) { 452 return; 453 } 454 int size = cur.size(); 455 if (size == 0) { 456 return; 457 } 458 459 if (cur instanceof ArrayMap) { 460 ArrayMap<K, V> arrayMap = (ArrayMap<K, V>) cur; 461 for (int i = 0; i < size; i++) { 462 action.accept(arrayMap.keyAt(i), arrayMap.valueAt(i)); 463 } 464 } else { 465 for (K key : cur.keySet()) { 466 action.accept(key, cur.get(key)); 467 } 468 } 469 } 470 471 /** 472 * @return the first element if not empty/null, null otherwise 473 */ firstOrNull(@ullable List<T> cur)474 public static @Nullable <T> T firstOrNull(@Nullable List<T> cur) { 475 return isEmpty(cur) ? null : cur.get(0); 476 } 477 478 /** 479 * @return the first element if not empty/null, null otherwise 480 */ firstOrNull(@ullable Collection<T> cur)481 public static @Nullable <T> T firstOrNull(@Nullable Collection<T> cur) { 482 return isEmpty(cur) ? null : cur.iterator().next(); 483 } 484 485 /** 486 * @return list of single given element if it's not null, empty list otherwise 487 */ singletonOrEmpty(@ullable T item)488 public static @NonNull <T> List<T> singletonOrEmpty(@Nullable T item) { 489 return item == null ? Collections.emptyList() : Collections.singletonList(item); 490 } 491 } 492