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