• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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