1 /* 2 * Copyright (C) 2020 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.net.module.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.util.ArrayMap; 22 import android.util.Pair; 23 import android.util.SparseArray; 24 25 import java.util.ArrayList; 26 import java.util.Collection; 27 import java.util.List; 28 import java.util.Objects; 29 import java.util.function.Function; 30 import java.util.function.Predicate; 31 32 /** 33 * Utilities for {@link Collection} and arrays. 34 * @hide 35 */ 36 public final class CollectionUtils { CollectionUtils()37 private CollectionUtils() {} 38 39 /** 40 * @return True if the array is null or 0-length. 41 */ isEmpty(@ullable T[] array)42 public static <T> boolean isEmpty(@Nullable T[] array) { 43 return array == null || array.length == 0; 44 } 45 46 /** 47 * @return True if the collection is null or 0-length. 48 */ isEmpty(@ullable Collection<T> collection)49 public static <T> boolean isEmpty(@Nullable Collection<T> collection) { 50 return collection == null || collection.isEmpty(); 51 } 52 53 /** 54 * Returns an int array from the given Integer list. 55 */ 56 @NonNull toIntArray(@onNull Collection<Integer> list)57 public static int[] toIntArray(@NonNull Collection<Integer> list) { 58 int[] array = new int[list.size()]; 59 int i = 0; 60 for (Integer item : list) { 61 array[i] = item; 62 i++; 63 } 64 return array; 65 } 66 67 /** 68 * Returns a long array from the given long list. 69 */ 70 @NonNull toLongArray(@onNull Collection<Long> list)71 public static long[] toLongArray(@NonNull Collection<Long> list) { 72 long[] array = new long[list.size()]; 73 int i = 0; 74 for (Long item : list) { 75 array[i] = item; 76 i++; 77 } 78 return array; 79 } 80 81 /** 82 * @return True if all elements satisfy the predicate, false otherwise. 83 * Note that means this always returns true for empty collections. 84 */ all(@onNull Collection<T> elem, @NonNull Predicate<T> predicate)85 public static <T> boolean all(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) { 86 for (final T e : elem) { 87 if (!predicate.test(e)) return false; 88 } 89 return true; 90 91 } 92 93 /** 94 * @return True if any element satisfies the predicate, false otherwise. 95 * Note that means this always returns false for empty collections. 96 */ any(@onNull Collection<T> elem, @NonNull Predicate<T> predicate)97 public static <T> boolean any(@NonNull Collection<T> elem, @NonNull Predicate<T> predicate) { 98 return indexOf(elem, predicate) >= 0; 99 } 100 101 /** 102 * @return The index of the first element that matches the predicate, or -1 if none. 103 */ indexOf(@onNull final Collection<T> elem, @NonNull final Predicate<? super T> predicate)104 public static <T> int indexOf(@NonNull final Collection<T> elem, 105 @NonNull final Predicate<? super T> predicate) { 106 int idx = 0; 107 for (final T e : elem) { 108 if (predicate.test(e)) return idx; 109 idx++; 110 } 111 return -1; 112 } 113 114 /** 115 * @return True if there exists at least one element in the sparse array for which 116 * condition {@code predicate} 117 */ any(@onNull SparseArray<T> array, @NonNull Predicate<T> predicate)118 public static <T> boolean any(@NonNull SparseArray<T> array, @NonNull Predicate<T> predicate) { 119 for (int i = 0; i < array.size(); ++i) { 120 if (predicate.test(array.valueAt(i))) { 121 return true; 122 } 123 } 124 return false; 125 } 126 127 /** 128 * @return true if the array contains the specified value. 129 */ contains(@ullable short[] array, short value)130 public static boolean contains(@Nullable short[] array, short value) { 131 if (array == null) return false; 132 for (int element : array) { 133 if (element == value) { 134 return true; 135 } 136 } 137 return false; 138 } 139 140 /** 141 * @return true if the array contains the specified value. 142 */ contains(@ullable int[] array, int value)143 public static boolean contains(@Nullable int[] array, int value) { 144 if (array == null) return false; 145 for (int element : array) { 146 if (element == value) { 147 return true; 148 } 149 } 150 return false; 151 } 152 153 /** 154 * @return true if the array contains the specified value. 155 */ contains(@ullable T[] array, @Nullable T value)156 public static <T> boolean contains(@Nullable T[] array, @Nullable T value) { 157 return indexOf(array, value) != -1; 158 } 159 160 /** 161 * Return first index of value in given array, or -1 if not found. 162 */ indexOf(@ullable T[] array, @Nullable T value)163 public static <T> int indexOf(@Nullable T[] array, @Nullable T value) { 164 if (array == null) return -1; 165 for (int i = 0; i < array.length; i++) { 166 if (Objects.equals(array[i], value)) return i; 167 } 168 return -1; 169 } 170 171 /** 172 * Returns the index of the needle array in the haystack array, or -1 if it can't be found. 173 * This is a byte array equivalent of Collections.indexOfSubList(). 174 */ indexOfSubArray(@onNull byte[] haystack, @NonNull byte[] needle)175 public static int indexOfSubArray(@NonNull byte[] haystack, @NonNull byte[] needle) { 176 for (int i = 0; i < haystack.length - needle.length + 1; i++) { 177 boolean found = true; 178 for (int j = 0; j < needle.length; j++) { 179 if (haystack[i + j] != needle[j]) { 180 found = false; 181 break; 182 } 183 } 184 if (found) { 185 return i; 186 } 187 } 188 return -1; 189 } 190 191 /** 192 * Returns a new collection of elements that match the passed predicate. 193 * @param source the elements to filter. 194 * @param test the predicate to test for. 195 * @return a new collection containing only the source elements that satisfy the predicate. 196 */ filter(@onNull final Collection<T> source, @NonNull final Predicate<T> test)197 @NonNull public static <T> ArrayList<T> filter(@NonNull final Collection<T> source, 198 @NonNull final Predicate<T> test) { 199 final ArrayList<T> matches = new ArrayList<>(); 200 for (final T e : source) { 201 if (test.test(e)) { 202 matches.add(e); 203 } 204 } 205 return matches; 206 } 207 208 /** 209 * Return sum of the given long array. 210 */ total(@ullable long[] array)211 public static long total(@Nullable long[] array) { 212 long total = 0; 213 if (array != null) { 214 for (long value : array) { 215 total += value; 216 } 217 } 218 return total; 219 } 220 221 /** 222 * Returns true if the first collection contains any of the elements of the second. 223 * @param haystack where to search 224 * @param needles what to search for 225 * @param <T> type of elements 226 * @return true if |haystack| contains any of the |needles|, false otherwise 227 */ containsAny(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)228 public static <T> boolean containsAny(@NonNull final Collection<T> haystack, 229 @NonNull final Collection<? extends T> needles) { 230 for (T needle : needles) { 231 if (haystack.contains(needle)) return true; 232 } 233 return false; 234 } 235 236 /** 237 * Returns true if the first collection contains all of the elements of the second. 238 * @param haystack where to search 239 * @param needles what to search for 240 * @param <T> type of elements 241 * @return true if |haystack| contains all of the |needles|, false otherwise 242 */ containsAll(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)243 public static <T> boolean containsAll(@NonNull final Collection<T> haystack, 244 @NonNull final Collection<? extends T> needles) { 245 return haystack.containsAll(needles); 246 } 247 248 /** 249 * Returns the first item of a collection that matches the predicate. 250 * @param haystack The collection to search. 251 * @param condition The predicate to match. 252 * @param <T> The type of element in the collection. 253 * @return The first element matching the predicate, or null if none. 254 */ 255 @Nullable findFirst(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)256 public static <T> T findFirst(@NonNull final Collection<T> haystack, 257 @NonNull final Predicate<? super T> condition) { 258 for (T needle : haystack) { 259 if (condition.test(needle)) return needle; 260 } 261 return null; 262 } 263 264 /** 265 * Returns the last item of a List that matches the predicate. 266 * @param haystack The List to search. 267 * @param condition The predicate to match. 268 * @param <T> The type of element in the list. 269 * @return The last element matching the predicate, or null if none. 270 */ 271 // There is no way to reverse iterate a Collection in Java (e.g. the collection may 272 // be a single-linked list), so implementing this on Collection is necessarily very 273 // wasteful (store and reverse a copy, test all elements, or recurse to the end of the 274 // list to test on the up path and possibly blow the call stack) 275 @Nullable findLast(@onNull final List<T> haystack, @NonNull final Predicate<? super T> condition)276 public static <T> T findLast(@NonNull final List<T> haystack, 277 @NonNull final Predicate<? super T> condition) { 278 for (int i = haystack.size() - 1; i >= 0; --i) { 279 final T needle = haystack.get(i); 280 if (condition.test(needle)) return needle; 281 } 282 return null; 283 } 284 285 /** 286 * Returns whether a collection contains an element matching a condition 287 * @param haystack The collection to search. 288 * @param condition The predicate to match. 289 * @param <T> The type of element in the collection. 290 * @return Whether the collection contains any element matching the condition. 291 */ contains(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)292 public static <T> boolean contains(@NonNull final Collection<T> haystack, 293 @NonNull final Predicate<? super T> condition) { 294 return -1 != indexOf(haystack, condition); 295 } 296 297 /** 298 * Standard map function, but returns a new modifiable ArrayList 299 * 300 * This returns a new list that contains, for each element of the source collection, its 301 * image through the passed transform. 302 * Elements in the source can be null if the transform accepts null inputs. 303 * Elements in the output can be null if the transform ever returns null. 304 * This function never returns null. If the source collection is empty, it returns the 305 * empty list. 306 * Contract : this method calls the transform function exactly once for each element in the 307 * list, in iteration order. 308 * 309 * @param source the source collection 310 * @param transform the function to transform the elements 311 * @param <T> type of source elements 312 * @param <R> type of destination elements 313 * @return an unmodifiable list of transformed elements 314 */ 315 @NonNull map(@onNull final Collection<T> source, @NonNull final Function<? super T, ? extends R> transform)316 public static <T, R> ArrayList<R> map(@NonNull final Collection<T> source, 317 @NonNull final Function<? super T, ? extends R> transform) { 318 final ArrayList<R> dest = new ArrayList<>(source.size()); 319 for (final T e : source) { 320 dest.add(transform.apply(e)); 321 } 322 return dest; 323 } 324 325 /** 326 * Standard zip function, but returns a new modifiable ArrayList 327 * 328 * This returns a list of pairs containing, at each position, a pair of the element from the 329 * first list at that index and the element from the second list at that index. 330 * Both lists must be the same size. They may contain null. 331 * 332 * The easiest way to visualize what's happening is to think of two lists being laid out next 333 * to each other and stitched together with a zipper. 334 * 335 * Contract : this method will read each element of each list exactly once, in some unspecified 336 * order. If it throws, it will not read any element. 337 * 338 * @param first the first list of elements 339 * @param second the second list of elements 340 * @param <T> the type of first elements 341 * @param <R> the type of second elements 342 * @return the zipped list 343 */ 344 @NonNull zip(@onNull final List<T> first, @NonNull final List<R> second)345 public static <T, R> ArrayList<Pair<T, R>> zip(@NonNull final List<T> first, 346 @NonNull final List<R> second) { 347 final int size = first.size(); 348 if (size != second.size()) { 349 throw new IllegalArgumentException("zip : collections must be the same size"); 350 } 351 final ArrayList<Pair<T, R>> dest = new ArrayList<>(size); 352 for (int i = 0; i < size; ++i) { 353 dest.add(new Pair<>(first.get(i), second.get(i))); 354 } 355 return dest; 356 } 357 358 /** 359 * Returns a new ArrayMap that associates each key with the value at the same index. 360 * 361 * Both lists must be the same size. 362 * Both keys and values may contain null. 363 * Keys may not contain the same value twice. 364 * 365 * Contract : this method will read each element of each list exactly once, but does not 366 * specify the order, except if it throws in which case the number of reads is undefined. 367 * 368 * @param keys The list of keys 369 * @param values The list of values 370 * @param <T> The type of keys 371 * @param <R> The type of values 372 * @return The associated map 373 */ 374 @NonNull assoc( @onNull final List<T> keys, @NonNull final List<R> values)375 public static <T, R> ArrayMap<T, R> assoc( 376 @NonNull final List<T> keys, @NonNull final List<R> values) { 377 final int size = keys.size(); 378 if (size != values.size()) { 379 throw new IllegalArgumentException("assoc : collections must be the same size"); 380 } 381 final ArrayMap<T, R> dest = new ArrayMap<>(size); 382 for (int i = 0; i < size; ++i) { 383 final T key = keys.get(i); 384 if (dest.containsKey(key)) { 385 throw new IllegalArgumentException( 386 "assoc : keys may not contain the same value twice"); 387 } 388 dest.put(key, values.get(i)); 389 } 390 return dest; 391 } 392 393 /** 394 * Returns an index of the given SparseArray that contains the given value, or -1 395 * number if no keys map to the given value. 396 * 397 * <p>Note this is a linear search, and if multiple keys can map to the same value 398 * then the smallest index is returned. 399 * 400 * <p>This function compares values with {@code equals} while the 401 * {@link SparseArray#indexOfValue} compares values using {@code ==}. 402 */ getIndexForValue(SparseArray<T> sparseArray, T value)403 public static <T> int getIndexForValue(SparseArray<T> sparseArray, T value) { 404 for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) { 405 T valueAt = sparseArray.valueAt(i); 406 if (valueAt == null) { 407 if (value == null) { 408 return i; 409 }; 410 } else if (valueAt.equals(value)) { 411 return i; 412 } 413 } 414 return -1; 415 } 416 } 417