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 ArrayUtils()43 private ArrayUtils() { /* cannot be instantiated */ } 44 newUnpaddedByteArray(int minLen)45 public static byte[] newUnpaddedByteArray(int minLen) { 46 return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen); 47 } 48 newUnpaddedCharArray(int minLen)49 public static char[] newUnpaddedCharArray(int minLen) { 50 return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen); 51 } 52 newUnpaddedIntArray(int minLen)53 public static int[] newUnpaddedIntArray(int minLen) { 54 return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen); 55 } 56 newUnpaddedBooleanArray(int minLen)57 public static boolean[] newUnpaddedBooleanArray(int minLen) { 58 return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen); 59 } 60 newUnpaddedLongArray(int minLen)61 public static long[] newUnpaddedLongArray(int minLen) { 62 return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen); 63 } 64 newUnpaddedFloatArray(int minLen)65 public static float[] newUnpaddedFloatArray(int minLen) { 66 return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen); 67 } 68 newUnpaddedObjectArray(int minLen)69 public static Object[] newUnpaddedObjectArray(int minLen) { 70 return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen); 71 } 72 73 @SuppressWarnings("unchecked") newUnpaddedArray(Class<T> clazz, int minLen)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 */ equals(byte[] array1, byte[] array2, int length)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") emptyArray(Class<T> kind)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 */ isEmpty(@ullable Collection<?> array)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 */ isEmpty(@ullable T[] array)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 */ isEmpty(@ullable int[] array)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 */ isEmpty(@ullable long[] array)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 */ isEmpty(@ullable byte[] array)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 */ isEmpty(@ullable boolean[] array)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 */ size(@ullable Object[] array)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 */ contains(@ullable T[] array, T value)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 */ indexOf(@ullable T[] array, T value)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 */ containsAll(@ullable T[] array, T[] check)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 */ containsAny(@ullable T[] array, T[] check)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 contains(@ullable int[] array, int value)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 contains(@ullable long[] array, long value)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 contains(@ullable char[] array, char value)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 */ containsAll(@ullable char[] array, char[] check)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 total(@ullable long[] array)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 convertToIntArray(List<Integer> list)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") appendElement(Class<T> kind, @Nullable T[] array, T element)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") appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)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") removeElement(Class<T> kind, @Nullable T[] array, T element)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 */ appendInt(@ullable int[] cur, int val, boolean allowDuplicates)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 */ appendInt(@ullable int[] cur, int val)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 */ removeInt(@ullable int[] cur, int val)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 */ removeString(@ullable String[] cur, String val)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 */ appendLong(@ullable long[] cur, long val)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 */ removeLong(@ullable long[] cur, long val)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 cloneOrNull(@ullable long[] array)459 public static @Nullable long[] cloneOrNull(@Nullable long[] array) { 460 return (array != null) ? array.clone() : null; 461 } 462 cloneOrNull(@ullable ArraySet<T> array)463 public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) { 464 return (array != null) ? new ArraySet<T>(array) : null; 465 } 466 add(@ullable ArraySet<T> cur, T val)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 remove(@ullable ArraySet<T> cur, T val)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 add(@ullable ArrayList<T> cur, T val)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 remove(@ullable ArrayList<T> cur, T val)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 contains(@ullable Collection<T> cur, T val)507 public static <T> boolean contains(@Nullable Collection<T> cur, T val) { 508 return (cur != null) ? cur.contains(val) : false; 509 } 510 trimToSize(@ullable T[] array, int size)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 */ referenceEquals(ArrayList<T> a, ArrayList<T> b)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 */ unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)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 defeatNullable(@ullable String[] val)587 public static @NonNull String[] defeatNullable(@Nullable String[] val) { 588 return (val != null) ? val : EmptyArray.STRING; 589 } 590 } 591