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