ArrayUtils.java revision 2abd66c4ffdb7905128b1ca245d4ccb97cbda1c8
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
17package com.android.internal.util;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.util.ArraySet;
22
23import dalvik.system.VMRuntime;
24
25import libcore.util.EmptyArray;
26
27import java.lang.reflect.Array;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.List;
33import 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 */
39public 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     * Length of the given array or 0 if it's null.
173     */
174    public static int size(@Nullable Object[] array) {
175        return array == null ? 0 : array.length;
176    }
177
178    /**
179     * Checks that value is present as at least one of the elements of the array.
180     * @param array the array to check in
181     * @param value the value to check for
182     * @return true if the value is present in the array
183     */
184    public static <T> boolean contains(@Nullable T[] array, T value) {
185        return indexOf(array, value) != -1;
186    }
187
188    /**
189     * Return first index of {@code value} in {@code array}, or {@code -1} if
190     * not found.
191     */
192    public static <T> int indexOf(@Nullable T[] array, T value) {
193        if (array == null) return -1;
194        for (int i = 0; i < array.length; i++) {
195            if (Objects.equals(array[i], value)) return i;
196        }
197        return -1;
198    }
199
200    /**
201     * Test if all {@code check} items are contained in {@code array}.
202     */
203    public static <T> boolean containsAll(@Nullable T[] array, T[] check) {
204        if (check == null) return true;
205        for (T checkItem : check) {
206            if (!contains(array, checkItem)) {
207                return false;
208            }
209        }
210        return true;
211    }
212
213    /**
214     * Test if any {@code check} items are contained in {@code array}.
215     */
216    public static <T> boolean containsAny(@Nullable T[] array, T[] check) {
217        if (check == null) return false;
218        for (T checkItem : check) {
219            if (contains(array, checkItem)) {
220                return true;
221            }
222        }
223        return false;
224    }
225
226    public static boolean contains(@Nullable int[] array, int value) {
227        if (array == null) return false;
228        for (int element : array) {
229            if (element == value) {
230                return true;
231            }
232        }
233        return false;
234    }
235
236    public static boolean contains(@Nullable long[] array, long value) {
237        if (array == null) return false;
238        for (long element : array) {
239            if (element == value) {
240                return true;
241            }
242        }
243        return false;
244    }
245
246    public static boolean contains(@Nullable char[] array, char value) {
247        if (array == null) return false;
248        for (char element : array) {
249            if (element == value) {
250                return true;
251            }
252        }
253        return false;
254    }
255
256    /**
257     * Test if all {@code check} items are contained in {@code array}.
258     */
259    public static <T> boolean containsAll(@Nullable char[] array, char[] check) {
260        if (check == null) return true;
261        for (char checkItem : check) {
262            if (!contains(array, checkItem)) {
263                return false;
264            }
265        }
266        return true;
267    }
268
269    public static long total(@Nullable long[] array) {
270        long total = 0;
271        if (array != null) {
272            for (long value : array) {
273                total += value;
274            }
275        }
276        return total;
277    }
278
279    public static int[] convertToIntArray(List<Integer> list) {
280        int[] array = new int[list.size()];
281        for (int i = 0; i < list.size(); i++) {
282            array[i] = list.get(i);
283        }
284        return array;
285    }
286
287    /**
288     * Adds value to given array if not already present, providing set-like
289     * behavior.
290     */
291    @SuppressWarnings("unchecked")
292    public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
293        return appendElement(kind, array, element, false);
294    }
295
296    /**
297     * Adds value to given array.
298     */
299    @SuppressWarnings("unchecked")
300    public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
301            boolean allowDuplicates) {
302        final T[] result;
303        final int end;
304        if (array != null) {
305            if (!allowDuplicates && contains(array, element)) return array;
306            end = array.length;
307            result = (T[])Array.newInstance(kind, end + 1);
308            System.arraycopy(array, 0, result, 0, end);
309        } else {
310            end = 0;
311            result = (T[])Array.newInstance(kind, 1);
312        }
313        result[end] = element;
314        return result;
315    }
316
317    /**
318     * Removes value from given array if present, providing set-like behavior.
319     */
320    @SuppressWarnings("unchecked")
321    public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) {
322        if (array != null) {
323            if (!contains(array, element)) return array;
324            final int length = array.length;
325            for (int i = 0; i < length; i++) {
326                if (Objects.equals(array[i], element)) {
327                    if (length == 1) {
328                        return null;
329                    }
330                    T[] result = (T[])Array.newInstance(kind, length - 1);
331                    System.arraycopy(array, 0, result, 0, i);
332                    System.arraycopy(array, i + 1, result, i, length - i - 1);
333                    return result;
334                }
335            }
336        }
337        return array;
338    }
339
340    /**
341     * Adds value to given array.
342     */
343    public static @NonNull int[] appendInt(@Nullable int[] cur, int val,
344            boolean allowDuplicates) {
345        if (cur == null) {
346            return new int[] { val };
347        }
348        final int N = cur.length;
349        if (!allowDuplicates) {
350            for (int i = 0; i < N; i++) {
351                if (cur[i] == val) {
352                    return cur;
353                }
354            }
355        }
356        int[] ret = new int[N + 1];
357        System.arraycopy(cur, 0, ret, 0, N);
358        ret[N] = val;
359        return ret;
360    }
361
362    /**
363     * Adds value to given array if not already present, providing set-like
364     * behavior.
365     */
366    public static @NonNull int[] appendInt(@Nullable int[] cur, int val) {
367        return appendInt(cur, val, false);
368    }
369
370    /**
371     * Removes value from given array if present, providing set-like behavior.
372     */
373    public static @Nullable int[] removeInt(@Nullable int[] cur, int val) {
374        if (cur == null) {
375            return null;
376        }
377        final int N = cur.length;
378        for (int i = 0; i < N; i++) {
379            if (cur[i] == val) {
380                int[] ret = new int[N - 1];
381                if (i > 0) {
382                    System.arraycopy(cur, 0, ret, 0, i);
383                }
384                if (i < (N - 1)) {
385                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
386                }
387                return ret;
388            }
389        }
390        return cur;
391    }
392
393    /**
394     * Removes value from given array if present, providing set-like behavior.
395     */
396    public static @Nullable String[] removeString(@Nullable String[] cur, String val) {
397        if (cur == null) {
398            return null;
399        }
400        final int N = cur.length;
401        for (int i = 0; i < N; i++) {
402            if (Objects.equals(cur[i], val)) {
403                String[] ret = new String[N - 1];
404                if (i > 0) {
405                    System.arraycopy(cur, 0, ret, 0, i);
406                }
407                if (i < (N - 1)) {
408                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
409                }
410                return ret;
411            }
412        }
413        return cur;
414    }
415
416    /**
417     * Adds value to given array if not already present, providing set-like
418     * behavior.
419     */
420    public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
421        if (cur == null) {
422            return new long[] { val };
423        }
424        final int N = cur.length;
425        for (int i = 0; i < N; i++) {
426            if (cur[i] == val) {
427                return cur;
428            }
429        }
430        long[] ret = new long[N + 1];
431        System.arraycopy(cur, 0, ret, 0, N);
432        ret[N] = val;
433        return ret;
434    }
435
436    /**
437     * Removes value from given array if present, providing set-like behavior.
438     */
439    public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
440        if (cur == null) {
441            return null;
442        }
443        final int N = cur.length;
444        for (int i = 0; i < N; i++) {
445            if (cur[i] == val) {
446                long[] ret = new long[N - 1];
447                if (i > 0) {
448                    System.arraycopy(cur, 0, ret, 0, i);
449                }
450                if (i < (N - 1)) {
451                    System.arraycopy(cur, i + 1, ret, i, N - i - 1);
452                }
453                return ret;
454            }
455        }
456        return cur;
457    }
458
459    public static @Nullable long[] cloneOrNull(@Nullable long[] array) {
460        return (array != null) ? array.clone() : null;
461    }
462
463    public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
464        return (array != null) ? new ArraySet<T>(array) : null;
465    }
466
467    public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
468        if (cur == null) {
469            cur = new ArraySet<>();
470        }
471        cur.add(val);
472        return cur;
473    }
474
475    public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) {
476        if (cur == null) {
477            return null;
478        }
479        cur.remove(val);
480        if (cur.isEmpty()) {
481            return null;
482        } else {
483            return cur;
484        }
485    }
486
487    public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) {
488        if (cur == null) {
489            cur = new ArrayList<>();
490        }
491        cur.add(val);
492        return cur;
493    }
494
495    public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
496        if (cur == null) {
497            return null;
498        }
499        cur.remove(val);
500        if (cur.isEmpty()) {
501            return null;
502        } else {
503            return cur;
504        }
505    }
506
507    public static <T> boolean contains(@Nullable Collection<T> cur, T val) {
508        return (cur != null) ? cur.contains(val) : false;
509    }
510
511    public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
512        if (array == null || size == 0) {
513            return null;
514        } else if (array.length == size) {
515            return array;
516        } else {
517            return Arrays.copyOf(array, size);
518        }
519    }
520
521    /**
522     * Returns true if the two ArrayLists are equal with respect to the objects they contain.
523     * The objects must be in the same order and be reference equal (== not .equals()).
524     */
525    public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) {
526        if (a == b) {
527            return true;
528        }
529
530        final int sizeA = a.size();
531        final int sizeB = b.size();
532        if (a == null || b == null || sizeA != sizeB) {
533            return false;
534        }
535
536        boolean diff = false;
537        for (int i = 0; i < sizeA && !diff; i++) {
538            diff |= a.get(i) != b.get(i);
539        }
540        return !diff;
541    }
542
543    /**
544     * Removes elements that match the predicate in an efficient way that alters the order of
545     * elements in the collection. This should only be used if order is not important.
546     * @param collection The ArrayList from which to remove elements.
547     * @param predicate The predicate that each element is tested against.
548     * @return the number of elements removed.
549     */
550    public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
551                                           @NonNull java.util.function.Predicate<T> predicate) {
552        if (collection == null) {
553            return 0;
554        }
555
556        final int size = collection.size();
557        int leftIdx = 0;
558        int rightIdx = size - 1;
559        while (leftIdx <= rightIdx) {
560            // Find the next element to remove moving left to right.
561            while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
562                leftIdx++;
563            }
564
565            // Find the next element to keep moving right to left.
566            while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
567                rightIdx--;
568            }
569
570            if (leftIdx >= rightIdx) {
571                // Done.
572                break;
573            }
574
575            Collections.swap(collection, leftIdx, rightIdx);
576            leftIdx++;
577            rightIdx--;
578        }
579
580        // leftIdx is now at the end.
581        for (int i = size - 1; i >= leftIdx; i--) {
582            collection.remove(i);
583        }
584        return size - leftIdx;
585    }
586
587    public static @NonNull String[] defeatNullable(@Nullable String[] val) {
588        return (val != null) ? val : EmptyArray.STRING;
589    }
590}
591