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 */ 104 @Nullable indexOf(@onNull final Collection<T> elem, @NonNull final Predicate<? super T> predicate)105 public static <T> int indexOf(@NonNull final Collection<T> elem, 106 @NonNull final Predicate<? super T> predicate) { 107 int idx = 0; 108 for (final T e : elem) { 109 if (predicate.test(e)) return idx; 110 idx++; 111 } 112 return -1; 113 } 114 115 /** 116 * @return True if there exists at least one element in the sparse array for which 117 * condition {@code predicate} 118 */ any(@onNull SparseArray<T> array, @NonNull Predicate<T> predicate)119 public static <T> boolean any(@NonNull SparseArray<T> array, @NonNull Predicate<T> predicate) { 120 for (int i = 0; i < array.size(); ++i) { 121 if (predicate.test(array.valueAt(i))) { 122 return true; 123 } 124 } 125 return false; 126 } 127 128 /** 129 * @return true if the array contains the specified value. 130 */ contains(@ullable short[] array, short value)131 public static boolean contains(@Nullable short[] array, short value) { 132 if (array == null) return false; 133 for (int element : array) { 134 if (element == value) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 /** 142 * @return true if the array contains the specified value. 143 */ contains(@ullable int[] array, int value)144 public static boolean contains(@Nullable int[] array, int value) { 145 if (array == null) return false; 146 for (int element : array) { 147 if (element == value) { 148 return true; 149 } 150 } 151 return false; 152 } 153 154 /** 155 * @return true if the array contains the specified value. 156 */ contains(@ullable T[] array, @Nullable T value)157 public static <T> boolean contains(@Nullable T[] array, @Nullable T value) { 158 return indexOf(array, value) != -1; 159 } 160 161 /** 162 * Return first index of value in given array, or -1 if not found. 163 */ indexOf(@ullable T[] array, @Nullable T value)164 public static <T> int indexOf(@Nullable T[] array, @Nullable T value) { 165 if (array == null) return -1; 166 for (int i = 0; i < array.length; i++) { 167 if (Objects.equals(array[i], value)) return i; 168 } 169 return -1; 170 } 171 172 /** 173 * Returns the index of the needle array in the haystack array, or -1 if it can't be found. 174 * This is a byte array equivalent of Collections.indexOfSubList(). 175 */ indexOfSubArray(@onNull byte[] haystack, @NonNull byte[] needle)176 public static int indexOfSubArray(@NonNull byte[] haystack, @NonNull byte[] needle) { 177 for (int i = 0; i < haystack.length - needle.length + 1; i++) { 178 boolean found = true; 179 for (int j = 0; j < needle.length; j++) { 180 if (haystack[i + j] != needle[j]) { 181 found = false; 182 break; 183 } 184 } 185 if (found) { 186 return i; 187 } 188 } 189 return -1; 190 } 191 192 /** 193 * Returns a new collection of elements that match the passed predicate. 194 * @param source the elements to filter. 195 * @param test the predicate to test for. 196 * @return a new collection containing only the source elements that satisfy the predicate. 197 */ filter(@onNull final Collection<T> source, @NonNull final Predicate<T> test)198 @NonNull public static <T> ArrayList<T> filter(@NonNull final Collection<T> source, 199 @NonNull final Predicate<T> test) { 200 final ArrayList<T> matches = new ArrayList<>(); 201 for (final T e : source) { 202 if (test.test(e)) { 203 matches.add(e); 204 } 205 } 206 return matches; 207 } 208 209 /** 210 * Return sum of the given long array. 211 */ total(@ullable long[] array)212 public static long total(@Nullable long[] array) { 213 long total = 0; 214 if (array != null) { 215 for (long value : array) { 216 total += value; 217 } 218 } 219 return total; 220 } 221 222 /** 223 * Returns true if the first collection contains any of the elements of the second. 224 * @param haystack where to search 225 * @param needles what to search for 226 * @param <T> type of elements 227 * @return true if |haystack| contains any of the |needles|, false otherwise 228 */ containsAny(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)229 public static <T> boolean containsAny(@NonNull final Collection<T> haystack, 230 @NonNull final Collection<? extends T> needles) { 231 for (T needle : needles) { 232 if (haystack.contains(needle)) return true; 233 } 234 return false; 235 } 236 237 /** 238 * Returns true if the first collection contains all of the elements of the second. 239 * @param haystack where to search 240 * @param needles what to search for 241 * @param <T> type of elements 242 * @return true if |haystack| contains all of the |needles|, false otherwise 243 */ containsAll(@onNull final Collection<T> haystack, @NonNull final Collection<? extends T> needles)244 public static <T> boolean containsAll(@NonNull final Collection<T> haystack, 245 @NonNull final Collection<? extends T> needles) { 246 return haystack.containsAll(needles); 247 } 248 249 /** 250 * Returns the first item of a collection that matches the predicate. 251 * @param haystack The collection to search. 252 * @param condition The predicate to match. 253 * @param <T> The type of element in the collection. 254 * @return The first element matching the predicate, or null if none. 255 */ 256 @Nullable findFirst(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)257 public static <T> T findFirst(@NonNull final Collection<T> haystack, 258 @NonNull final Predicate<? super T> condition) { 259 for (T needle : haystack) { 260 if (condition.test(needle)) return needle; 261 } 262 return null; 263 } 264 265 /** 266 * Returns the last item of a List that matches the predicate. 267 * @param haystack The List to search. 268 * @param condition The predicate to match. 269 * @param <T> The type of element in the list. 270 * @return The last element matching the predicate, or null if none. 271 */ 272 // There is no way to reverse iterate a Collection in Java (e.g. the collection may 273 // be a single-linked list), so implementing this on Collection is necessarily very 274 // wasteful (store and reverse a copy, test all elements, or recurse to the end of the 275 // list to test on the up path and possibly blow the call stack) 276 @Nullable findLast(@onNull final List<T> haystack, @NonNull final Predicate<? super T> condition)277 public static <T> T findLast(@NonNull final List<T> haystack, 278 @NonNull final Predicate<? super T> condition) { 279 for (int i = haystack.size() - 1; i >= 0; --i) { 280 final T needle = haystack.get(i); 281 if (condition.test(needle)) return needle; 282 } 283 return null; 284 } 285 286 /** 287 * Returns whether a collection contains an element matching a condition 288 * @param haystack The collection to search. 289 * @param condition The predicate to match. 290 * @param <T> The type of element in the collection. 291 * @return Whether the collection contains any element matching the condition. 292 */ contains(@onNull final Collection<T> haystack, @NonNull final Predicate<? super T> condition)293 public static <T> boolean contains(@NonNull final Collection<T> haystack, 294 @NonNull final Predicate<? super T> condition) { 295 return -1 != indexOf(haystack, condition); 296 } 297 298 /** 299 * Standard map function, but returns a new modifiable ArrayList 300 * 301 * This returns a new list that contains, for each element of the source collection, its 302 * image through the passed transform. 303 * Elements in the source can be null if the transform accepts null inputs. 304 * Elements in the output can be null if the transform ever returns null. 305 * This function never returns null. If the source collection is empty, it returns the 306 * empty list. 307 * Contract : this method calls the transform function exactly once for each element in the 308 * list, in iteration order. 309 * 310 * @param source the source collection 311 * @param transform the function to transform the elements 312 * @param <T> type of source elements 313 * @param <R> type of destination elements 314 * @return an unmodifiable list of transformed elements 315 */ 316 @NonNull map(@onNull final Collection<T> source, @NonNull final Function<? super T, ? extends R> transform)317 public static <T, R> ArrayList<R> map(@NonNull final Collection<T> source, 318 @NonNull final Function<? super T, ? extends R> transform) { 319 final ArrayList<R> dest = new ArrayList<>(source.size()); 320 for (final T e : source) { 321 dest.add(transform.apply(e)); 322 } 323 return dest; 324 } 325 326 /** 327 * Standard zip function, but returns a new modifiable ArrayList 328 * 329 * This returns a list of pairs containing, at each position, a pair of the element from the 330 * first list at that index and the element from the second list at that index. 331 * Both lists must be the same size. They may contain null. 332 * 333 * The easiest way to visualize what's happening is to think of two lists being laid out next 334 * to each other and stitched together with a zipper. 335 * 336 * Contract : this method will read each element of each list exactly once, in some unspecified 337 * order. If it throws, it will not read any element. 338 * 339 * @param first the first list of elements 340 * @param second the second list of elements 341 * @param <T> the type of first elements 342 * @param <R> the type of second elements 343 * @return the zipped list 344 */ 345 @NonNull zip(@onNull final List<T> first, @NonNull final List<R> second)346 public static <T, R> ArrayList<Pair<T, R>> zip(@NonNull final List<T> first, 347 @NonNull final List<R> second) { 348 final int size = first.size(); 349 if (size != second.size()) { 350 throw new IllegalArgumentException("zip : collections must be the same size"); 351 } 352 final ArrayList<Pair<T, R>> dest = new ArrayList<>(size); 353 for (int i = 0; i < size; ++i) { 354 dest.add(new Pair<>(first.get(i), second.get(i))); 355 } 356 return dest; 357 } 358 359 /** 360 * Returns a new ArrayMap that associates each key with the value at the same index. 361 * 362 * Both lists must be the same size. 363 * Both keys and values may contain null. 364 * Keys may not contain the same value twice. 365 * 366 * Contract : this method will read each element of each list exactly once, but does not 367 * specify the order, except if it throws in which case the number of reads is undefined. 368 * 369 * @param keys The list of keys 370 * @param values The list of values 371 * @param <T> The type of keys 372 * @param <R> The type of values 373 * @return The associated map 374 */ 375 @NonNull assoc( @onNull final List<T> keys, @NonNull final List<R> values)376 public static <T, R> ArrayMap<T, R> assoc( 377 @NonNull final List<T> keys, @NonNull final List<R> values) { 378 final int size = keys.size(); 379 if (size != values.size()) { 380 throw new IllegalArgumentException("assoc : collections must be the same size"); 381 } 382 final ArrayMap<T, R> dest = new ArrayMap<>(size); 383 for (int i = 0; i < size; ++i) { 384 final T key = keys.get(i); 385 if (dest.containsKey(key)) { 386 throw new IllegalArgumentException( 387 "assoc : keys may not contain the same value twice"); 388 } 389 dest.put(key, values.get(i)); 390 } 391 return dest; 392 } 393 } 394