1 /*
2  * Copyright (C) 2006 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
23 import android.util.ArraySet;
24 
25 import dalvik.system.VMRuntime;
26 
27 import libcore.util.EmptyArray;
28 
29 import java.io.File;
30 import java.lang.reflect.Array;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.Set;
39 import java.util.function.IntFunction;
40 
41 /**
42  * Static utility methods for arrays that aren't already included in {@link java.util.Arrays}.
43  */
44 public class ArrayUtils {
45     private static final int CACHE_SIZE = 73;
46     private static Object[] sCache = new Object[CACHE_SIZE];
47 
48     public static final File[] EMPTY_FILE = new File[0];
49 
ArrayUtils()50     private ArrayUtils() { /* cannot be instantiated */ }
51 
newUnpaddedByteArray(int minLen)52     public static byte[] newUnpaddedByteArray(int minLen) {
53         return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
54     }
55 
newUnpaddedCharArray(int minLen)56     public static char[] newUnpaddedCharArray(int minLen) {
57         return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
58     }
59 
60     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
newUnpaddedIntArray(int minLen)61     public static int[] newUnpaddedIntArray(int minLen) {
62         return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
63     }
64 
newUnpaddedBooleanArray(int minLen)65     public static boolean[] newUnpaddedBooleanArray(int minLen) {
66         return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
67     }
68 
newUnpaddedLongArray(int minLen)69     public static long[] newUnpaddedLongArray(int minLen) {
70         return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
71     }
72 
newUnpaddedFloatArray(int minLen)73     public static float[] newUnpaddedFloatArray(int minLen) {
74         return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
75     }
76 
newUnpaddedObjectArray(int minLen)77     public static Object[] newUnpaddedObjectArray(int minLen) {
78         return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
79     }
80 
81     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
82     @SuppressWarnings("unchecked")
newUnpaddedArray(Class<T> clazz, int minLen)83     public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
84         return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
85     }
86 
87     /**
88      * Checks if the beginnings of two byte arrays are equal.
89      *
90      * @param array1 the first byte array
91      * @param array2 the second byte array
92      * @param length the number of bytes to check
93      * @return true if they're equal, false otherwise
94      */
equals(byte[] array1, byte[] array2, int length)95     public static boolean equals(byte[] array1, byte[] array2, int length) {
96         if (length < 0) {
97             throw new IllegalArgumentException();
98         }
99 
100         if (array1 == array2) {
101             return true;
102         }
103         if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
104             return false;
105         }
106         for (int i = 0; i < length; i++) {
107             if (array1[i] != array2[i]) {
108                 return false;
109             }
110         }
111         return true;
112     }
113 
114     /**
115      * Returns an empty array of the specified type.  The intent is that
116      * it will return the same empty array every time to avoid reallocation,
117      * although this is not guaranteed.
118      */
119     @UnsupportedAppUsage
120     @SuppressWarnings("unchecked")
emptyArray(Class<T> kind)121     public static <T> T[] emptyArray(Class<T> kind) {
122         if (kind == Object.class) {
123             return (T[]) EmptyArray.OBJECT;
124         }
125 
126         int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE;
127         Object cache = sCache[bucket];
128 
129         if (cache == null || cache.getClass().getComponentType() != kind) {
130             cache = Array.newInstance(kind, 0);
131             sCache[bucket] = cache;
132 
133             // Log.e("cache", "new empty " + kind.getName() + " at " + bucket);
134         }
135 
136         return (T[]) cache;
137     }
138 
139     /**
140      * Returns the same array or an empty one if it's null.
141      */
emptyIfNull(@ullable T[] items, Class<T> kind)142     public static @NonNull <T> T[] emptyIfNull(@Nullable T[] items, Class<T> kind) {
143         return items != null ? items : emptyArray(kind);
144     }
145 
146     /**
147      * Checks if given array is null or has zero elements.
148      */
isEmpty(@ullable Collection<?> array)149     public static boolean isEmpty(@Nullable Collection<?> array) {
150         return array == null || array.isEmpty();
151     }
152 
153     /**
154      * Checks if given map is null or has zero elements.
155      */
isEmpty(@ullable Map<?, ?> map)156     public static boolean isEmpty(@Nullable Map<?, ?> map) {
157         return map == null || map.isEmpty();
158     }
159 
160     /**
161      * Checks if given array is null or has zero elements.
162      */
163     @UnsupportedAppUsage
isEmpty(@ullable T[] array)164     public static <T> boolean isEmpty(@Nullable T[] array) {
165         return array == null || array.length == 0;
166     }
167 
168     /**
169      * Checks if given array is null or has zero elements.
170      */
isEmpty(@ullable int[] array)171     public static boolean isEmpty(@Nullable int[] array) {
172         return array == null || array.length == 0;
173     }
174 
175     /**
176      * Checks if given array is null or has zero elements.
177      */
isEmpty(@ullable long[] array)178     public static boolean isEmpty(@Nullable long[] array) {
179         return array == null || array.length == 0;
180     }
181 
182     /**
183      * Checks if given array is null or has zero elements.
184      */
isEmpty(@ullable byte[] array)185     public static boolean isEmpty(@Nullable byte[] array) {
186         return array == null || array.length == 0;
187     }
188 
189     /**
190      * Checks if given array is null or has zero elements.
191      */
isEmpty(@ullable boolean[] array)192     public static boolean isEmpty(@Nullable boolean[] array) {
193         return array == null || array.length == 0;
194     }
195 
196     /**
197      * Length of the given array or 0 if it's null.
198      */
size(@ullable Object[] array)199     public static int size(@Nullable Object[] array) {
200         return array == null ? 0 : array.length;
201     }
202 
203     /**
204      * Length of the given collection or 0 if it's null.
205      */
size(@ullable Collection<?> collection)206     public static int size(@Nullable Collection<?> collection) {
207         return collection == null ? 0 : collection.size();
208     }
209 
210     /**
211      * Length of the given map or 0 if it's null.
212      */
size(@ullable Map<?, ?> map)213     public static int size(@Nullable Map<?, ?> map) {
214         return map == null ? 0 : map.size();
215     }
216 
217     /**
218      * Checks that value is present as at least one of the elements of the array.
219      * @param array the array to check in
220      * @param value the value to check for
221      * @return true if the value is present in the array
222      */
223     @UnsupportedAppUsage
contains(@ullable T[] array, T value)224     public static <T> boolean contains(@Nullable T[] array, T value) {
225         return indexOf(array, value) != -1;
226     }
227 
228     /**
229      * Return first index of {@code value} in {@code array}, or {@code -1} if
230      * not found.
231      */
232     @UnsupportedAppUsage
indexOf(@ullable T[] array, T value)233     public static <T> int indexOf(@Nullable T[] array, T value) {
234         if (array == null) return -1;
235         for (int i = 0; i < array.length; i++) {
236             if (Objects.equals(array[i], value)) return i;
237         }
238         return -1;
239     }
240 
241     /**
242      * Test if all {@code check} items are contained in {@code array}.
243      */
containsAll(@ullable T[] array, T[] check)244     public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
245         if (check == null) return true;
246         for (T checkItem : check) {
247             if (!contains(array, checkItem)) {
248                 return false;
249             }
250         }
251         return true;
252     }
253 
254     /**
255      * Test if any {@code check} items are contained in {@code array}.
256      */
containsAny(@ullable T[] array, T[] check)257     public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
258         if (check == null) return false;
259         for (T checkItem : check) {
260             if (contains(array, checkItem)) {
261                 return true;
262             }
263         }
264         return false;
265     }
266 
267     @UnsupportedAppUsage
contains(@ullable int[] array, int value)268     public static boolean contains(@Nullable int[] array, int value) {
269         if (array == null) return false;
270         for (int element : array) {
271             if (element == value) {
272                 return true;
273             }
274         }
275         return false;
276     }
277 
contains(@ullable long[] array, long value)278     public static boolean contains(@Nullable long[] array, long value) {
279         if (array == null) return false;
280         for (long element : array) {
281             if (element == value) {
282                 return true;
283             }
284         }
285         return false;
286     }
287 
contains(@ullable char[] array, char value)288     public static boolean contains(@Nullable char[] array, char value) {
289         if (array == null) return false;
290         for (char element : array) {
291             if (element == value) {
292                 return true;
293             }
294         }
295         return false;
296     }
297 
298     /**
299      * Test if all {@code check} items are contained in {@code array}.
300      */
containsAll(@ullable char[] array, char[] check)301     public static <T> boolean containsAll(@Nullable char[] array, char[] check) {
302         if (check == null) return true;
303         for (char checkItem : check) {
304             if (!contains(array, checkItem)) {
305                 return false;
306             }
307         }
308         return true;
309     }
310 
total(@ullable long[] array)311     public static long total(@Nullable long[] array) {
312         long total = 0;
313         if (array != null) {
314             for (long value : array) {
315                 total += value;
316             }
317         }
318         return total;
319     }
320 
321     /**
322      * @deprecated use {@code IntArray} instead
323      */
324     @Deprecated
convertToIntArray(List<Integer> list)325     public static int[] convertToIntArray(List<Integer> list) {
326         int[] array = new int[list.size()];
327         for (int i = 0; i < list.size(); i++) {
328             array[i] = list.get(i);
329         }
330         return array;
331     }
332 
333     @NonNull
convertToIntArray(@onNull ArraySet<Integer> set)334     public static int[] convertToIntArray(@NonNull ArraySet<Integer> set) {
335         final int size = set.size();
336         int[] array = new int[size];
337         for (int i = 0; i < size; i++) {
338             array[i] = set.valueAt(i);
339         }
340         return array;
341     }
342 
convertToLongArray(@ullable int[] intArray)343     public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) {
344         if (intArray == null) return null;
345         long[] array = new long[intArray.length];
346         for (int i = 0; i < intArray.length; i++) {
347             array[i] = (long) intArray[i];
348         }
349         return array;
350     }
351 
352     /**
353      * Returns the concatenation of the given arrays.  Only works for object arrays, not for
354      * primitive arrays.  See {@link #concat(byte[]...)} for a variant that works on byte arrays.
355      *
356      * @param kind The class of the array elements
357      * @param arrays The arrays to concatenate.  Null arrays are treated as empty.
358      * @param <T> The class of the array elements (inferred from kind).
359      * @return A single array containing all the elements of the parameter arrays.
360      */
361     @SuppressWarnings("unchecked")
concat(Class<T> kind, @Nullable T[]... arrays)362     public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[]... arrays) {
363         if (arrays == null || arrays.length == 0) {
364             return createEmptyArray(kind);
365         }
366 
367         int totalLength = 0;
368         for (T[] item : arrays) {
369             if (item == null) {
370                 continue;
371             }
372 
373             totalLength += item.length;
374         }
375 
376         // Optimization for entirely empty arrays.
377         if (totalLength == 0) {
378             return createEmptyArray(kind);
379         }
380 
381         final T[] all = (T[]) Array.newInstance(kind, totalLength);
382         int pos = 0;
383         for (T[] item : arrays) {
384             if (item == null || item.length == 0) {
385                 continue;
386             }
387             System.arraycopy(item, 0, all, pos, item.length);
388             pos += item.length;
389         }
390         return all;
391     }
392 
createEmptyArray(Class<T> kind)393     private static @NonNull <T> T[] createEmptyArray(Class<T> kind) {
394         if (kind == String.class) {
395             return (T[]) EmptyArray.STRING;
396         } else if (kind == Object.class) {
397             return (T[]) EmptyArray.OBJECT;
398         }
399 
400         return (T[]) Array.newInstance(kind, 0);
401     }
402 
403     /**
404      * Returns the concatenation of the given byte arrays.  Null arrays are treated as empty.
405      */
concat(@ullable byte[]... arrays)406     public static @NonNull byte[] concat(@Nullable byte[]... arrays) {
407         if (arrays == null) {
408             return new byte[0];
409         }
410         int totalLength = 0;
411         for (byte[] a : arrays) {
412             if (a != null) {
413                 totalLength += a.length;
414             }
415         }
416         final byte[] result = new byte[totalLength];
417         int pos = 0;
418         for (byte[] a : arrays) {
419             if (a != null) {
420                 System.arraycopy(a, 0, result, pos, a.length);
421                 pos += a.length;
422             }
423         }
424         return result;
425     }
426 
427     /**
428      * Adds value to given array if not already present, providing set-like
429      * behavior.
430      */
431     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
432     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element)433     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
434         return appendElement(kind, array, element, false);
435     }
436 
437     /**
438      * Adds value to given array.
439      */
440     @SuppressWarnings("unchecked")
appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)441     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
442             boolean allowDuplicates) {
443         final T[] result;
444         final int end;
445         if (array != null) {
446             if (!allowDuplicates && contains(array, element)) return array;
447             end = array.length;
448             result = (T[])Array.newInstance(kind, end + 1);
449             System.arraycopy(array, 0, result, 0, end);
450         } else {
451             end = 0;
452             result = (T[])Array.newInstance(kind, 1);
453         }
454         result[end] = element;
455         return result;
456     }
457 
458     /**
459      * Removes value from given array if present, providing set-like behavior.
460      */
461     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
462     @SuppressWarnings("unchecked")
removeElement(Class<T> kind, @Nullable T[] array, T element)463     public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
464         if (array != null) {
465             if (!contains(array, element)) return array;
466             final int length = array.length;
467             for (int i = 0; i < length; i++) {
468                 if (Objects.equals(array[i], element)) {
469                     if (length == 1) {
470                         return null;
471                     }
472                     T[] result = (T[])Array.newInstance(kind, length - 1);
473                     System.arraycopy(array, 0, result, 0, i);
474                     System.arraycopy(array, i + 1, result, i, length - i - 1);
475                     return result;
476                 }
477             }
478         }
479         return array;
480     }
481 
482     /**
483      * Adds value to given array.
484      */
appendInt(@ullable int[] cur, int val, boolean allowDuplicates)485     public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
486             boolean allowDuplicates) {
487         if (cur == null) {
488             return new int[] { val };
489         }
490         final int N = cur.length;
491         if (!allowDuplicates) {
492             for (int i = 0; i < N; i++) {
493                 if (cur[i] == val) {
494                     return cur;
495                 }
496             }
497         }
498         int[] ret = new int[N + 1];
499         System.arraycopy(cur, 0, ret, 0, N);
500         ret[N] = val;
501         return ret;
502     }
503 
504     /**
505      * Adds value to given array if not already present, providing set-like
506      * behavior.
507      */
508     @UnsupportedAppUsage
appendInt(@ullable int[] cur, int val)509     public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
510         return appendInt(cur, val, false);
511     }
512 
513     /**
514      * Removes value from given array if present, providing set-like behavior.
515      */
removeInt(@ullable int[] cur, int val)516     public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
517         if (cur == null) {
518             return null;
519         }
520         final int N = cur.length;
521         for (int i = 0; i < N; i++) {
522             if (cur[i] == val) {
523                 int[] ret = new int[N - 1];
524                 if (i > 0) {
525                     System.arraycopy(cur, 0, ret, 0, i);
526                 }
527                 if (i < (N - 1)) {
528                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
529                 }
530                 return ret;
531             }
532         }
533         return cur;
534     }
535 
536     /**
537      * Removes value from given array if present, providing set-like behavior.
538      */
removeString(@ullable String[] cur, String val)539     public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
540         if (cur == null) {
541             return null;
542         }
543         final int N = cur.length;
544         for (int i = 0; i < N; i++) {
545             if (Objects.equals(cur[i], val)) {
546                 String[] ret = new String[N - 1];
547                 if (i > 0) {
548                     System.arraycopy(cur, 0, ret, 0, i);
549                 }
550                 if (i < (N - 1)) {
551                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
552                 }
553                 return ret;
554             }
555         }
556         return cur;
557     }
558 
559     /**
560      * Adds value to given array if not already present, providing set-like
561      * behavior.
562      */
appendLong(@ullable long[] cur, long val, boolean allowDuplicates)563     public static @NonNull long[] appendLong(@Nullable long[] cur, long val,
564             boolean allowDuplicates) {
565         if (cur == null) {
566             return new long[] { val };
567         }
568         final int N = cur.length;
569         if (!allowDuplicates) {
570             for (int i = 0; i < N; i++) {
571                 if (cur[i] == val) {
572                     return cur;
573                 }
574             }
575         }
576         long[] ret = new long[N + 1];
577         System.arraycopy(cur, 0, ret, 0, N);
578         ret[N] = val;
579         return ret;
580     }
581 
582     /**
583      * Adds value to given array if not already present, providing set-like
584      * behavior.
585      */
appendLong(@ullable long[] cur, long val)586     public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
587         return appendLong(cur, val, false);
588     }
589 
590     /**
591      * Removes value from given array if present, providing set-like behavior.
592      */
removeLong(@ullable long[] cur, long val)593     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
594         if (cur == null) {
595             return null;
596         }
597         final int N = cur.length;
598         for (int i = 0; i < N; i++) {
599             if (cur[i] == val) {
600                 long[] ret = new long[N - 1];
601                 if (i > 0) {
602                     System.arraycopy(cur, 0, ret, 0, i);
603                 }
604                 if (i < (N - 1)) {
605                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
606                 }
607                 return ret;
608             }
609         }
610         return cur;
611     }
612 
cloneOrNull(@ullable long[] array)613     public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
614         return (array != null) ? array.clone() : null;
615     }
616 
617     /**
618      * Clones an array or returns null if the array is null.
619      */
cloneOrNull(@ullable T[] array)620     public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) {
621         return (array != null) ? array.clone() : null;
622     }
623 
cloneOrNull(@ullable ArraySet<T> array)624     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
625         return (array != null) ? new ArraySet<T>(array) : null;
626     }
627 
add(@ullable ArraySet<T> cur, T val)628     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
629         if (cur == null) {
630             cur = new ArraySet<>();
631         }
632         cur.add(val);
633         return cur;
634     }
635 
636     /**
637      * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}.
638      */
addAll(@ullable ArraySet<T> cur, @Nullable Collection<T> val)639     public static @NonNull <T> ArraySet<T> addAll(@Nullable ArraySet<T> cur,
640             @Nullable Collection<T> val) {
641         if (cur == null) {
642             cur = new ArraySet<>();
643         }
644         if (val != null) {
645             cur.addAll(val);
646         }
647         return cur;
648     }
649 
remove(@ullable ArraySet<T> cur, T val)650     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
651         if (cur == null) {
652             return null;
653         }
654         cur.remove(val);
655         if (cur.isEmpty()) {
656             return null;
657         } else {
658             return cur;
659         }
660     }
661 
add(@ullable ArrayList<T> cur, T val)662     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
663         if (cur == null) {
664             cur = new ArrayList<>();
665         }
666         cur.add(val);
667         return cur;
668     }
669 
add(@ullable ArrayList<T> cur, int index, T val)670     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, int index, T val) {
671         if (cur == null) {
672             cur = new ArrayList<>();
673         }
674         cur.add(index, val);
675         return cur;
676     }
677 
remove(@ullable ArrayList<T> cur, T val)678     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
679         if (cur == null) {
680             return null;
681         }
682         cur.remove(val);
683         if (cur.isEmpty()) {
684             return null;
685         } else {
686             return cur;
687         }
688     }
689 
contains(@ullable Collection<T> cur, T val)690     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
691         return (cur != null) ? cur.contains(val) : false;
692     }
693 
trimToSize(@ullable T[] array, int size)694     public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
695         if (array == null || size == 0) {
696             return null;
697         } else if (array.length == size) {
698             return array;
699         } else {
700             return Arrays.copyOf(array, size);
701         }
702     }
703 
704     /**
705      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
706      * The objects must be in the same order and be reference equal (== not .equals()).
707      */
referenceEquals(ArrayList<T> a, ArrayList<T> b)708     public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
709         if (a == b) {
710             return true;
711         }
712 
713         final int sizeA = a.size();
714         final int sizeB = b.size();
715         if (a == null || b == null || sizeA != sizeB) {
716             return false;
717         }
718 
719         boolean diff = false;
720         for (int i = 0; i < sizeA && !diff; i++) {
721             diff |= a.get(i) != b.get(i);
722         }
723         return !diff;
724     }
725 
726     /**
727      * Removes elements that match the predicate in an efficient way that alters the order of
728      * elements in the collection. This should only be used if order is not important.
729      * @param collection The ArrayList from which to remove elements.
730      * @param predicate The predicate that each element is tested against.
731      * @return the number of elements removed.
732      */
unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)733     public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
734                                            @NonNull java.util.function.Predicate<T> predicate) {
735         if (collection == null) {
736             return 0;
737         }
738 
739         final int size = collection.size();
740         int leftIdx = 0;
741         int rightIdx = size - 1;
742         while (leftIdx <= rightIdx) {
743             // Find the next element to remove moving left to right.
744             while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
745                 leftIdx++;
746             }
747 
748             // Find the next element to keep moving right to left.
749             while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
750                 rightIdx--;
751             }
752 
753             if (leftIdx >= rightIdx) {
754                 // Done.
755                 break;
756             }
757 
758             Collections.swap(collection, leftIdx, rightIdx);
759             leftIdx++;
760             rightIdx--;
761         }
762 
763         // leftIdx is now at the end.
764         for (int i = size - 1; i >= leftIdx; i--) {
765             collection.remove(i);
766         }
767         return size - leftIdx;
768     }
769 
defeatNullable(@ullable int[] val)770     public static @NonNull int[] defeatNullable(@Nullable int[] val) {
771         return (val != null) ? val : EmptyArray.INT;
772     }
773 
defeatNullable(@ullable String[] val)774     public static @NonNull String[] defeatNullable(@Nullable String[] val) {
775         return (val != null) ? val : EmptyArray.STRING;
776     }
777 
defeatNullable(@ullable File[] val)778     public static @NonNull File[] defeatNullable(@Nullable File[] val) {
779         return (val != null) ? val : EMPTY_FILE;
780     }
781 
782     /**
783      * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
784      *
785      * @param len length of the array. Must be non-negative
786      * @param index the index to check
787      * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
788      */
checkBounds(int len, int index)789     public static void checkBounds(int len, int index) {
790         if (index < 0 || len <= index) {
791             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
792         }
793     }
794 
795     /**
796      * Throws {@link ArrayIndexOutOfBoundsException} if the range is out of bounds.
797      * @param len length of the array. Must be non-negative
798      * @param offset start index of the range. Must be non-negative
799      * @param count length of the range. Must be non-negative
800      * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length
801      * {@code count} is out of bounds of the array
802      */
throwsIfOutOfBounds(int len, int offset, int count)803     public static void throwsIfOutOfBounds(int len, int offset, int count) {
804         if (len < 0) {
805             throw new ArrayIndexOutOfBoundsException("Negative length: " + len);
806         }
807 
808         if ((offset | count) < 0 || offset > len - count) {
809             throw new ArrayIndexOutOfBoundsException(
810                     "length=" + len + "; regionStart=" + offset + "; regionLength=" + count);
811         }
812     }
813 
814     /**
815      * Returns an array with values from {@code val} minus {@code null} values
816      *
817      * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
818      */
filterNotNull(T[] val, IntFunction<T[]> arrayConstructor)819     public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
820         int nullCount = 0;
821         int size = size(val);
822         for (int i = 0; i < size; i++) {
823             if (val[i] == null) {
824                 nullCount++;
825             }
826         }
827         if (nullCount == 0) {
828             return val;
829         }
830         T[] result = arrayConstructor.apply(size - nullCount);
831         int outIdx = 0;
832         for (int i = 0; i < size; i++) {
833             if (val[i] != null) {
834                 result[outIdx++] = val[i];
835             }
836         }
837         return result;
838     }
839 
840     /**
841      * Returns an array containing elements from the given one that match the given predicate.
842      * The returned array may, in some cases, be the reference to the input array.
843      */
filter(@ullable T[] items, @NonNull IntFunction<T[]> arrayConstructor, @NonNull java.util.function.Predicate<T> predicate)844     public static @Nullable <T> T[] filter(@Nullable T[] items,
845             @NonNull IntFunction<T[]> arrayConstructor,
846             @NonNull java.util.function.Predicate<T> predicate) {
847         if (isEmpty(items)) {
848             return items;
849         }
850 
851         int matchesCount = 0;
852         int size = size(items);
853         final boolean[] tests = new boolean[size];
854         for (int i = 0; i < size; i++) {
855             tests[i] = predicate.test(items[i]);
856             if (tests[i]) {
857                 matchesCount++;
858             }
859         }
860         if (matchesCount == items.length) {
861             return items;
862         }
863         T[] result = arrayConstructor.apply(matchesCount);
864         if (matchesCount == 0) {
865             return result;
866         }
867         int outIdx = 0;
868         for (int i = 0; i < size; i++) {
869             if (tests[i]) {
870                 result[outIdx++] = items[i];
871             }
872         }
873         return result;
874     }
875 
startsWith(byte[] cur, byte[] val)876     public static boolean startsWith(byte[] cur, byte[] val) {
877         if (cur == null || val == null) return false;
878         if (cur.length < val.length) return false;
879         for (int i = 0; i < val.length; i++) {
880             if (cur[i] != val[i]) return false;
881         }
882         return true;
883     }
884 
885     /**
886      * Returns the first element from the array for which
887      * condition {@code predicate} is true, or null if there is no such element
888      */
find(@ullable T[] items, @NonNull java.util.function.Predicate<T> predicate)889     public static @Nullable <T> T find(@Nullable T[] items,
890             @NonNull java.util.function.Predicate<T> predicate) {
891         if (isEmpty(items)) return null;
892         for (final T item : items) {
893             if (predicate.test(item)) return item;
894         }
895         return null;
896     }
897 
deepToString(Object value)898     public static String deepToString(Object value) {
899         if (value != null && value.getClass().isArray()) {
900             if (value.getClass() == boolean[].class) {
901                 return Arrays.toString((boolean[]) value);
902             } else if (value.getClass() == byte[].class) {
903                 return Arrays.toString((byte[]) value);
904             } else if (value.getClass() == char[].class) {
905                 return Arrays.toString((char[]) value);
906             } else if (value.getClass() == double[].class) {
907                 return Arrays.toString((double[]) value);
908             } else if (value.getClass() == float[].class) {
909                 return Arrays.toString((float[]) value);
910             } else if (value.getClass() == int[].class) {
911                 return Arrays.toString((int[]) value);
912             } else if (value.getClass() == long[].class) {
913                 return Arrays.toString((long[]) value);
914             } else if (value.getClass() == short[].class) {
915                 return Arrays.toString((short[]) value);
916             } else {
917                 return Arrays.deepToString((Object[]) value);
918             }
919         } else {
920             return String.valueOf(value);
921         }
922     }
923 
924     /**
925      * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code
926      * null}, otherwise returns {@code null}.
927      */
928     @Nullable
getOrNull(@ullable T[] items, int i)929     public static <T> T getOrNull(@Nullable T[] items, int i) {
930         return (items != null && items.length > i) ? items[i] : null;
931     }
932 
firstOrNull(T[] items)933     public static @Nullable <T> T firstOrNull(T[] items) {
934         return items.length > 0 ? items[0] : null;
935     }
936 
937     /**
938      * Creates a {@link List} from an array. Different from {@link Arrays#asList(Object[])} as that
939      * will use the parameter as the backing array, meaning changes are not isolated.
940      */
toList(T[] array)941     public static <T> List<T> toList(T[] array) {
942         List<T> list = new ArrayList<>(array.length);
943         //noinspection ManualArrayToCollectionCopy
944         for (T item : array) {
945             //noinspection UseBulkOperation
946             list.add(item);
947         }
948         return list;
949     }
950 }
951