Home | History | Annotate | Download | only in util
      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.util.ArraySet;
     22 
     23 import dalvik.system.VMRuntime;
     24 
     25 import libcore.util.EmptyArray;
     26 
     27 import java.lang.reflect.Array;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.Collection;
     31 import java.util.Collections;
     32 import java.util.List;
     33 import java.util.Objects;
     34 
     35 /**
     36  * ArrayUtils contains some methods that you can call to find out
     37  * the most efficient increments by which to grow arrays.
     38  */
     39 public class ArrayUtils {
     40     private static final int CACHE_SIZE = 73;
     41     private static Object[] sCache = new Object[CACHE_SIZE];
     42 
     43     private ArrayUtils() { /* cannot be instantiated */ }
     44 
     45     public static byte[] newUnpaddedByteArray(int minLen) {
     46         return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
     47     }
     48 
     49     public static char[] newUnpaddedCharArray(int minLen) {
     50         return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
     51     }
     52 
     53     public static int[] newUnpaddedIntArray(int minLen) {
     54         return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
     55     }
     56 
     57     public static boolean[] newUnpaddedBooleanArray(int minLen) {
     58         return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
     59     }
     60 
     61     public static long[] newUnpaddedLongArray(int minLen) {
     62         return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
     63     }
     64 
     65     public static float[] newUnpaddedFloatArray(int minLen) {
     66         return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
     67     }
     68 
     69     public static Object[] newUnpaddedObjectArray(int minLen) {
     70         return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
     71     }
     72 
     73     @SuppressWarnings("unchecked")
     74     public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
     75         return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
     76     }
     77 
     78     /**
     79      * Checks if the beginnings of two byte arrays are equal.
     80      *
     81      * @param array1 the first byte array
     82      * @param array2 the second byte array
     83      * @param length the number of bytes to check
     84      * @return true if they're equal, false otherwise
     85      */
     86     public static boolean equals(byte[] array1, byte[] array2, int length) {
     87         if (length < 0) {
     88             throw new IllegalArgumentException();
     89         }
     90 
     91         if (array1 == array2) {
     92             return true;
     93         }
     94         if (array1 == null || array2 == null || array1.length < length || array2.length < length) {
     95             return false;
     96         }
     97         for (int i = 0; i < length; i++) {
     98             if (array1[i] != array2[i]) {
     99                 return false;
    100             }
    101         }
    102         return true;
    103     }
    104 
    105     /**
    106      * Returns an empty array of the specified type.  The intent is that
    107      * it will return the same empty array every time to avoid reallocation,
    108      * although this is not guaranteed.
    109      */
    110     @SuppressWarnings("unchecked")
    111     public static <T> T[] emptyArray(Class<T> kind) {
    112         if (kind == Object.class) {
    113             return (T[]) EmptyArray.OBJECT;
    114         }
    115 
    116         int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE;
    117         Object cache = sCache[bucket];
    118 
    119         if (cache == null || cache.getClass().getComponentType() != kind) {
    120             cache = Array.newInstance(kind, 0);
    121             sCache[bucket] = cache;
    122 
    123             // Log.e("cache", "new empty " + kind.getName() + " at " + bucket);
    124         }
    125 
    126         return (T[]) cache;
    127     }
    128 
    129     /**
    130      * Checks if given array is null or has zero elements.
    131      */
    132     public static boolean isEmpty(@Nullable Collection<?> array) {
    133         return array == null || array.isEmpty();
    134     }
    135 
    136     /**
    137      * Checks if given array is null or has zero elements.
    138      */
    139     public static <T> boolean isEmpty(@Nullable T[] array) {
    140         return array == null || array.length == 0;
    141     }
    142 
    143     /**
    144      * Checks if given array is null or has zero elements.
    145      */
    146     public static boolean isEmpty(@Nullable int[] array) {
    147         return array == null || array.length == 0;
    148     }
    149 
    150     /**
    151      * Checks if given array is null or has zero elements.
    152      */
    153     public static boolean isEmpty(@Nullable long[] array) {
    154         return array == null || array.length == 0;
    155     }
    156 
    157     /**
    158      * Checks if given array is null or has zero elements.
    159      */
    160     public static boolean isEmpty(@Nullable byte[] array) {
    161         return array == null || array.length == 0;
    162     }
    163 
    164     /**
    165      * Checks if given array is null or has zero elements.
    166      */
    167     public static boolean isEmpty(@Nullable boolean[] array) {
    168         return array == null || array.length == 0;
    169     }
    170 
    171     /**
    172      * Checks that value is present as at least one of the elements of the array.
    173      * @param array the array to check in
    174      * @param value the value to check for
    175      * @return true if the value is present in the array
    176      */
    177     public static <T> boolean contains(@Nullable T[] array, T value) {
    178         return indexOf(array, value) != -1;
    179     }
    180 
    181     /**
    182      * Return first index of {@code value} in {@code array}, or {@code -1} if
    183      * not found.
    184      */
    185     public static <T> int indexOf(@Nullable T[] array, T value) {
    186         if (array == null) return -1;
    187         for (int i = 0; i < array.length; i++) {
    188             if (Objects.equals(array[i], value)) return i;
    189         }
    190         return -1;
    191     }
    192 
    193     /**
    194      * Test if all {@code check} items are contained in {@code array}.
    195      */
    196     public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
    197         if (check == null) return true;
    198         for (T checkItem : check) {
    199             if (!contains(array, checkItem)) {
    200                 return false;
    201             }
    202         }
    203         return true;
    204     }
    205 
    206     /**
    207      * Test if any {@code check} items are contained in {@code array}.
    208      */
    209     public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
    210         if (check == null) return false;
    211         for (T checkItem : check) {
    212             if (contains(array, checkItem)) {
    213                 return true;
    214             }
    215         }
    216         return false;
    217     }
    218 
    219     public static boolean contains(@Nullable int[] array, int value) {
    220         if (array == null) return false;
    221         for (int element : array) {
    222             if (element == value) {
    223                 return true;
    224             }
    225         }
    226         return false;
    227     }
    228 
    229     public static boolean contains(@Nullable long[] array, long value) {
    230         if (array == null) return false;
    231         for (long element : array) {
    232             if (element == value) {
    233                 return true;
    234             }
    235         }
    236         return false;
    237     }
    238 
    239     public static long total(@Nullable long[] array) {
    240         long total = 0;
    241         if (array != null) {
    242             for (long value : array) {
    243                 total += value;
    244             }
    245         }
    246         return total;
    247     }
    248 
    249     public static int[] convertToIntArray(List<Integer> list) {
    250         int[] array = new int[list.size()];
    251         for (int i = 0; i < list.size(); i++) {
    252             array[i] = list.get(i);
    253         }
    254         return array;
    255     }
    256 
    257     /**
    258      * Adds value to given array if not already present, providing set-like
    259      * behavior.
    260      */
    261     @SuppressWarnings("unchecked")
    262     public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
    263         final T[] result;
    264         final int end;
    265         if (array != null) {
    266             if (contains(array, element)) return array;
    267             end = array.length;
    268             result = (T[])Array.newInstance(kind, end + 1);
    269             System.arraycopy(array, 0, result, 0, end);
    270         } else {
    271             end = 0;
    272             result = (T[])Array.newInstance(kind, 1);
    273         }
    274         result[end] = element;
    275         return result;
    276     }
    277 
    278     /**
    279      * Removes value from given array if present, providing set-like behavior.
    280      */
    281     @SuppressWarnings("unchecked")
    282     public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
    283         if (array != null) {
    284             if (!contains(array, element)) return array;
    285             final int length = array.length;
    286             for (int i = 0; i < length; i++) {
    287                 if (Objects.equals(array[i], element)) {
    288                     if (length == 1) {
    289                         return null;
    290                     }
    291                     T[] result = (T[])Array.newInstance(kind, length - 1);
    292                     System.arraycopy(array, 0, result, 0, i);
    293                     System.arraycopy(array, i + 1, result, i, length - i - 1);
    294                     return result;
    295                 }
    296             }
    297         }
    298         return array;
    299     }
    300 
    301     /**
    302      * Adds value to given array if not already present, providing set-like
    303      * behavior.
    304      */
    305     public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
    306         if (cur == null) {
    307             return new int[] { val };
    308         }
    309         final int N = cur.length;
    310         for (int i = 0; i < N; i++) {
    311             if (cur[i] == val) {
    312                 return cur;
    313             }
    314         }
    315         int[] ret = new int[N + 1];
    316         System.arraycopy(cur, 0, ret, 0, N);
    317         ret[N] = val;
    318         return ret;
    319     }
    320 
    321     /**
    322      * Removes value from given array if present, providing set-like behavior.
    323      */
    324     public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
    325         if (cur == null) {
    326             return null;
    327         }
    328         final int N = cur.length;
    329         for (int i = 0; i < N; i++) {
    330             if (cur[i] == val) {
    331                 int[] ret = new int[N - 1];
    332                 if (i > 0) {
    333                     System.arraycopy(cur, 0, ret, 0, i);
    334                 }
    335                 if (i < (N - 1)) {
    336                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
    337                 }
    338                 return ret;
    339             }
    340         }
    341         return cur;
    342     }
    343 
    344     /**
    345      * Removes value from given array if present, providing set-like behavior.
    346      */
    347     public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
    348         if (cur == null) {
    349             return null;
    350         }
    351         final int N = cur.length;
    352         for (int i = 0; i < N; i++) {
    353             if (Objects.equals(cur[i], val)) {
    354                 String[] ret = new String[N - 1];
    355                 if (i > 0) {
    356                     System.arraycopy(cur, 0, ret, 0, i);
    357                 }
    358                 if (i < (N - 1)) {
    359                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
    360                 }
    361                 return ret;
    362             }
    363         }
    364         return cur;
    365     }
    366 
    367     /**
    368      * Adds value to given array if not already present, providing set-like
    369      * behavior.
    370      */
    371     public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
    372         if (cur == null) {
    373             return new long[] { val };
    374         }
    375         final int N = cur.length;
    376         for (int i = 0; i < N; i++) {
    377             if (cur[i] == val) {
    378                 return cur;
    379             }
    380         }
    381         long[] ret = new long[N + 1];
    382         System.arraycopy(cur, 0, ret, 0, N);
    383         ret[N] = val;
    384         return ret;
    385     }
    386 
    387     /**
    388      * Removes value from given array if present, providing set-like behavior.
    389      */
    390     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
    391         if (cur == null) {
    392             return null;
    393         }
    394         final int N = cur.length;
    395         for (int i = 0; i < N; i++) {
    396             if (cur[i] == val) {
    397                 long[] ret = new long[N - 1];
    398                 if (i > 0) {
    399                     System.arraycopy(cur, 0, ret, 0, i);
    400                 }
    401                 if (i < (N - 1)) {
    402                     System.arraycopy(cur, i + 1, ret, i, N - i - 1);
    403                 }
    404                 return ret;
    405             }
    406         }
    407         return cur;
    408     }
    409 
    410     public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
    411         return (array != null) ? array.clone() : null;
    412     }
    413 
    414     public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
    415         return (array != null) ? new ArraySet<T>(array) : null;
    416     }
    417 
    418     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
    419         if (cur == null) {
    420             cur = new ArraySet<>();
    421         }
    422         cur.add(val);
    423         return cur;
    424     }
    425 
    426     public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
    427         if (cur == null) {
    428             return null;
    429         }
    430         cur.remove(val);
    431         if (cur.isEmpty()) {
    432             return null;
    433         } else {
    434             return cur;
    435         }
    436     }
    437 
    438     public static <T> boolean contains(@Nullable ArraySet<T> cur, T val) {
    439         return (cur != null) ? cur.contains(val) : false;
    440     }
    441 
    442     public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
    443         if (cur == null) {
    444             cur = new ArrayList<>();
    445         }
    446         cur.add(val);
    447         return cur;
    448     }
    449 
    450     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
    451         if (cur == null) {
    452             return null;
    453         }
    454         cur.remove(val);
    455         if (cur.isEmpty()) {
    456             return null;
    457         } else {
    458             return cur;
    459         }
    460     }
    461 
    462     public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
    463         return (cur != null) ? cur.contains(val) : false;
    464     }
    465 
    466     public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
    467         if (array == null || size == 0) {
    468             return null;
    469         } else if (array.length == size) {
    470             return array;
    471         } else {
    472             return Arrays.copyOf(array, size);
    473         }
    474     }
    475 
    476     /**
    477      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
    478      * The objects must be in the same order and be reference equal (== not .equals()).
    479      */
    480     public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
    481         if (a == b) {
    482             return true;
    483         }
    484 
    485         final int sizeA = a.size();
    486         final int sizeB = b.size();
    487         if (a == null || b == null || sizeA != sizeB) {
    488             return false;
    489         }
    490 
    491         boolean diff = false;
    492         for (int i = 0; i < sizeA && !diff; i++) {
    493             diff |= a.get(i) != b.get(i);
    494         }
    495         return !diff;
    496     }
    497 
    498     /**
    499      * Removes elements that match the predicate in an efficient way that alters the order of
    500      * elements in the collection. This should only be used if order is not important.
    501      * @param collection The ArrayList from which to remove elements.
    502      * @param predicate The predicate that each element is tested against.
    503      * @return the number of elements removed.
    504      */
    505     public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
    506                                            @NonNull java.util.function.Predicate<T> predicate) {
    507         if (collection == null) {
    508             return 0;
    509         }
    510 
    511         final int size = collection.size();
    512         int leftIdx = 0;
    513         int rightIdx = size - 1;
    514         while (leftIdx <= rightIdx) {
    515             // Find the next element to remove moving left to right.
    516             while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
    517                 leftIdx++;
    518             }
    519 
    520             // Find the next element to keep moving right to left.
    521             while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
    522                 rightIdx--;
    523             }
    524 
    525             if (leftIdx >= rightIdx) {
    526                 // Done.
    527                 break;
    528             }
    529 
    530             Collections.swap(collection, leftIdx, rightIdx);
    531             leftIdx++;
    532             rightIdx--;
    533         }
    534 
    535         // leftIdx is now at the end.
    536         for (int i = size - 1; i >= leftIdx; i--) {
    537             collection.remove(i);
    538         }
    539         return size - leftIdx;
    540     }
    541 }
    542