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