ArrayUtils.java revision a8bbd76d9b5249c64ef31aa162e9a84abaad39ba
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     * 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