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