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.compat.annotation.UnsupportedAppUsage; 22 import android.os.Build; 23 import android.util.ArraySet; 24 import android.util.EmptyArray; 25 26 import dalvik.system.VMRuntime; 27 28 import java.io.File; 29 import java.lang.reflect.Array; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Objects; 37 import java.util.Set; 38 import java.util.function.IntFunction; 39 40 /** 41 * Static utility methods for arrays that aren't already included in {@link java.util.Arrays}. 42 */ 43 @android.ravenwood.annotation.RavenwoodKeepWholeClass 44 public class ArrayUtils { 45 private static final int CACHE_SIZE = 73; 46 private static Object[] sCache = new Object[CACHE_SIZE]; 47 48 public static final File[] EMPTY_FILE = new File[0]; 49 ArrayUtils()50 private ArrayUtils() { /* cannot be instantiated */ } 51 52 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedByteArray(int minLen)53 public static byte[] newUnpaddedByteArray(int minLen) { 54 return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen); 55 } 56 57 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedCharArray(int minLen)58 public static char[] newUnpaddedCharArray(int minLen) { 59 return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen); 60 } 61 62 @android.ravenwood.annotation.RavenwoodReplace 63 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) newUnpaddedIntArray(int minLen)64 public static int[] newUnpaddedIntArray(int minLen) { 65 return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen); 66 } 67 68 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedBooleanArray(int minLen)69 public static boolean[] newUnpaddedBooleanArray(int minLen) { 70 return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen); 71 } 72 73 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedLongArray(int minLen)74 public static long[] newUnpaddedLongArray(int minLen) { 75 return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen); 76 } 77 78 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedFloatArray(int minLen)79 public static float[] newUnpaddedFloatArray(int minLen) { 80 return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen); 81 } 82 83 @android.ravenwood.annotation.RavenwoodReplace newUnpaddedObjectArray(int minLen)84 public static Object[] newUnpaddedObjectArray(int minLen) { 85 return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen); 86 } 87 88 @android.ravenwood.annotation.RavenwoodReplace 89 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 90 @SuppressWarnings("unchecked") newUnpaddedArray(Class<T> clazz, int minLen)91 public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) { 92 return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen); 93 } 94 newUnpaddedByteArray$ravenwood(int minLen)95 public static byte[] newUnpaddedByteArray$ravenwood(int minLen) { 96 return new byte[minLen]; 97 } 98 newUnpaddedCharArray$ravenwood(int minLen)99 public static char[] newUnpaddedCharArray$ravenwood(int minLen) { 100 return new char[minLen]; 101 } 102 newUnpaddedIntArray$ravenwood(int minLen)103 public static int[] newUnpaddedIntArray$ravenwood(int minLen) { 104 return new int[minLen]; 105 } 106 newUnpaddedBooleanArray$ravenwood(int minLen)107 public static boolean[] newUnpaddedBooleanArray$ravenwood(int minLen) { 108 return new boolean[minLen]; 109 } 110 newUnpaddedLongArray$ravenwood(int minLen)111 public static long[] newUnpaddedLongArray$ravenwood(int minLen) { 112 return new long[minLen]; 113 } 114 newUnpaddedFloatArray$ravenwood(int minLen)115 public static float[] newUnpaddedFloatArray$ravenwood(int minLen) { 116 return new float[minLen]; 117 } 118 newUnpaddedObjectArray$ravenwood(int minLen)119 public static Object[] newUnpaddedObjectArray$ravenwood(int minLen) { 120 return new Object[minLen]; 121 } 122 newUnpaddedArray$ravenwood(Class<T> clazz, int minLen)123 public static <T> T[] newUnpaddedArray$ravenwood(Class<T> clazz, int minLen) { 124 return (T[]) Array.newInstance(clazz, minLen); 125 } 126 127 /** 128 * Checks if the beginnings of two byte arrays are equal. 129 * 130 * @param array1 the first byte array 131 * @param array2 the second byte array 132 * @param length the number of bytes to check 133 * @return true if they're equal, false otherwise 134 */ equals(byte[] array1, byte[] array2, int length)135 public static boolean equals(byte[] array1, byte[] array2, int length) { 136 if (length < 0) { 137 throw new IllegalArgumentException(); 138 } 139 140 if (array1 == array2) { 141 return true; 142 } 143 if (array1 == null || array2 == null || array1.length < length || array2.length < length) { 144 return false; 145 } 146 for (int i = 0; i < length; i++) { 147 if (array1[i] != array2[i]) { 148 return false; 149 } 150 } 151 return true; 152 } 153 154 /** 155 * Returns an empty array of the specified type. The intent is that 156 * it will return the same empty array every time to avoid reallocation, 157 * although this is not guaranteed. 158 */ 159 @UnsupportedAppUsage 160 @SuppressWarnings("unchecked") emptyArray(Class<T> kind)161 public static <T> T[] emptyArray(Class<T> kind) { 162 if (kind == Object.class) { 163 return (T[]) EmptyArray.OBJECT; 164 } 165 166 int bucket = (kind.hashCode() & 0x7FFFFFFF) % CACHE_SIZE; 167 Object cache = sCache[bucket]; 168 169 if (cache == null || cache.getClass().getComponentType() != kind) { 170 cache = Array.newInstance(kind, 0); 171 sCache[bucket] = cache; 172 173 // Log.e("cache", "new empty " + kind.getName() + " at " + bucket); 174 } 175 176 return (T[]) cache; 177 } 178 179 /** 180 * Returns the same array or an empty one if it's null. 181 */ emptyIfNull(@ullable T[] items, Class<T> kind)182 public static @NonNull <T> T[] emptyIfNull(@Nullable T[] items, Class<T> kind) { 183 return items != null ? items : emptyArray(kind); 184 } 185 186 /** 187 * Checks if given array is null or has zero elements. 188 */ isEmpty(@ullable Collection<?> array)189 public static boolean isEmpty(@Nullable Collection<?> array) { 190 return array == null || array.isEmpty(); 191 } 192 193 /** 194 * Checks if given map is null or has zero elements. 195 */ isEmpty(@ullable Map<?, ?> map)196 public static boolean isEmpty(@Nullable Map<?, ?> map) { 197 return map == null || map.isEmpty(); 198 } 199 200 /** 201 * Checks if given array is null or has zero elements. 202 */ 203 @UnsupportedAppUsage isEmpty(@ullable T[] array)204 public static <T> boolean isEmpty(@Nullable T[] array) { 205 return array == null || array.length == 0; 206 } 207 208 /** 209 * Checks if given array is null or has zero elements. 210 */ isEmpty(@ullable int[] array)211 public static boolean isEmpty(@Nullable int[] array) { 212 return array == null || array.length == 0; 213 } 214 215 /** 216 * Checks if given array is null or has zero elements. 217 */ isEmpty(@ullable long[] array)218 public static boolean isEmpty(@Nullable long[] array) { 219 return array == null || array.length == 0; 220 } 221 222 /** 223 * Checks if given array is null or has zero elements. 224 */ isEmpty(@ullable byte[] array)225 public static boolean isEmpty(@Nullable byte[] array) { 226 return array == null || array.length == 0; 227 } 228 229 /** 230 * Checks if given array is null or has zero elements. 231 */ isEmpty(@ullable boolean[] array)232 public static boolean isEmpty(@Nullable boolean[] array) { 233 return array == null || array.length == 0; 234 } 235 236 /** 237 * Length of the given array or 0 if it's null. 238 */ size(@ullable Object[] array)239 public static int size(@Nullable Object[] array) { 240 return array == null ? 0 : array.length; 241 } 242 243 /** 244 * Length of the given collection or 0 if it's null. 245 */ size(@ullable Collection<?> collection)246 public static int size(@Nullable Collection<?> collection) { 247 return collection == null ? 0 : collection.size(); 248 } 249 250 /** 251 * Length of the given map or 0 if it's null. 252 */ size(@ullable Map<?, ?> map)253 public static int size(@Nullable Map<?, ?> map) { 254 return map == null ? 0 : map.size(); 255 } 256 257 /** 258 * Checks that value is present as at least one of the elements of the array. 259 * @param array the array to check in 260 * @param value the value to check for 261 * @return true if the value is present in the array 262 */ 263 @UnsupportedAppUsage contains(@ullable T[] array, T value)264 public static <T> boolean contains(@Nullable T[] array, T value) { 265 return indexOf(array, value) != -1; 266 } 267 268 /** 269 * Return first index of {@code value} in {@code array}, or {@code -1} if 270 * not found. 271 */ 272 @UnsupportedAppUsage indexOf(@ullable T[] array, T value)273 public static <T> int indexOf(@Nullable T[] array, T value) { 274 if (array == null) return -1; 275 for (int i = 0; i < array.length; i++) { 276 if (Objects.equals(array[i], value)) return i; 277 } 278 return -1; 279 } 280 281 /** 282 * Test if all {@code check} items are contained in {@code array}. 283 */ containsAll(@ullable T[] array, T[] check)284 public static <T> boolean containsAll(@Nullable T[] array, T[] check) { 285 if (check == null) return true; 286 for (T checkItem : check) { 287 if (!contains(array, checkItem)) { 288 return false; 289 } 290 } 291 return true; 292 } 293 294 /** 295 * Test if any {@code check} items are contained in {@code array}. 296 */ containsAny(@ullable T[] array, T[] check)297 public static <T> boolean containsAny(@Nullable T[] array, T[] check) { 298 if (check == null) return false; 299 for (T checkItem : check) { 300 if (contains(array, checkItem)) { 301 return true; 302 } 303 } 304 return false; 305 } 306 307 @UnsupportedAppUsage contains(@ullable int[] array, int value)308 public static boolean contains(@Nullable int[] array, int value) { 309 if (array == null) return false; 310 for (int element : array) { 311 if (element == value) { 312 return true; 313 } 314 } 315 return false; 316 } 317 contains(@ullable long[] array, long value)318 public static boolean contains(@Nullable long[] array, long value) { 319 if (array == null) return false; 320 for (long element : array) { 321 if (element == value) { 322 return true; 323 } 324 } 325 return false; 326 } 327 contains(@ullable char[] array, char value)328 public static boolean contains(@Nullable char[] array, char value) { 329 if (array == null) return false; 330 for (char element : array) { 331 if (element == value) { 332 return true; 333 } 334 } 335 return false; 336 } 337 338 /** 339 * Test if all {@code check} items are contained in {@code array}. 340 */ containsAll(@ullable char[] array, char[] check)341 public static <T> boolean containsAll(@Nullable char[] array, char[] check) { 342 if (check == null) return true; 343 for (char checkItem : check) { 344 if (!contains(array, checkItem)) { 345 return false; 346 } 347 } 348 return true; 349 } 350 total(@ullable long[] array)351 public static long total(@Nullable long[] array) { 352 long total = 0; 353 if (array != null) { 354 for (long value : array) { 355 total += value; 356 } 357 } 358 return total; 359 } 360 361 /** 362 * @deprecated use {@code IntArray} instead 363 */ 364 @Deprecated convertToIntArray(List<Integer> list)365 public static int[] convertToIntArray(List<Integer> list) { 366 int[] array = new int[list.size()]; 367 for (int i = 0; i < list.size(); i++) { 368 array[i] = list.get(i); 369 } 370 return array; 371 } 372 373 @NonNull convertToIntArray(@onNull ArraySet<Integer> set)374 public static int[] convertToIntArray(@NonNull ArraySet<Integer> set) { 375 final int size = set.size(); 376 int[] array = new int[size]; 377 for (int i = 0; i < size; i++) { 378 array[i] = set.valueAt(i); 379 } 380 return array; 381 } 382 convertToLongArray(@ullable int[] intArray)383 public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) { 384 if (intArray == null) return null; 385 long[] array = new long[intArray.length]; 386 for (int i = 0; i < intArray.length; i++) { 387 array[i] = (long) intArray[i]; 388 } 389 return array; 390 } 391 392 /** 393 * Returns the concatenation of the given arrays. Only works for object arrays, not for 394 * primitive arrays. See {@link #concat(byte[]...)} for a variant that works on byte arrays. 395 * 396 * @param kind The class of the array elements 397 * @param arrays The arrays to concatenate. Null arrays are treated as empty. 398 * @param <T> The class of the array elements (inferred from kind). 399 * @return A single array containing all the elements of the parameter arrays. 400 */ 401 @SuppressWarnings("unchecked") concat(Class<T> kind, @Nullable T[]... arrays)402 public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[]... arrays) { 403 if (arrays == null || arrays.length == 0) { 404 return createEmptyArray(kind); 405 } 406 407 int totalLength = 0; 408 for (T[] item : arrays) { 409 if (item == null) { 410 continue; 411 } 412 413 totalLength += item.length; 414 } 415 416 // Optimization for entirely empty arrays. 417 if (totalLength == 0) { 418 return createEmptyArray(kind); 419 } 420 421 final T[] all = (T[]) Array.newInstance(kind, totalLength); 422 int pos = 0; 423 for (T[] item : arrays) { 424 if (item == null || item.length == 0) { 425 continue; 426 } 427 System.arraycopy(item, 0, all, pos, item.length); 428 pos += item.length; 429 } 430 return all; 431 } 432 createEmptyArray(Class<T> kind)433 private static @NonNull <T> T[] createEmptyArray(Class<T> kind) { 434 if (kind == String.class) { 435 return (T[]) EmptyArray.STRING; 436 } else if (kind == Object.class) { 437 return (T[]) EmptyArray.OBJECT; 438 } 439 440 return (T[]) Array.newInstance(kind, 0); 441 } 442 443 /** 444 * Returns the concatenation of the given byte arrays. Null arrays are treated as empty. 445 */ concat(@ullable byte[]... arrays)446 public static @NonNull byte[] concat(@Nullable byte[]... arrays) { 447 if (arrays == null) { 448 return new byte[0]; 449 } 450 int totalLength = 0; 451 for (byte[] a : arrays) { 452 if (a != null) { 453 totalLength += a.length; 454 } 455 } 456 final byte[] result = new byte[totalLength]; 457 int pos = 0; 458 for (byte[] a : arrays) { 459 if (a != null) { 460 System.arraycopy(a, 0, result, pos, a.length); 461 pos += a.length; 462 } 463 } 464 return result; 465 } 466 467 /** 468 * Adds value to given array if not already present, providing set-like 469 * behavior. 470 */ 471 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 472 @SuppressWarnings("unchecked") appendElement(Class<T> kind, @Nullable T[] array, T element)473 public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) { 474 return appendElement(kind, array, element, false); 475 } 476 477 /** 478 * Adds value to given array. 479 */ 480 @SuppressWarnings("unchecked") appendElement(Class<T> kind, @Nullable T[] array, T element, boolean allowDuplicates)481 public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element, 482 boolean allowDuplicates) { 483 final T[] result; 484 final int end; 485 if (array != null) { 486 if (!allowDuplicates && contains(array, element)) return array; 487 end = array.length; 488 result = (T[])Array.newInstance(kind, end + 1); 489 System.arraycopy(array, 0, result, 0, end); 490 } else { 491 end = 0; 492 result = (T[])Array.newInstance(kind, 1); 493 } 494 result[end] = element; 495 return result; 496 } 497 498 /** 499 * Removes value from given array if present, providing set-like behavior. 500 */ 501 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 502 @SuppressWarnings("unchecked") removeElement(Class<T> kind, @Nullable T[] array, T element)503 public static @Nullable <T> T[] removeElement(Class<T> kind, @Nullable T[] array, T element) { 504 if (array != null) { 505 if (!contains(array, element)) return array; 506 final int length = array.length; 507 for (int i = 0; i < length; i++) { 508 if (Objects.equals(array[i], element)) { 509 if (length == 1) { 510 return null; 511 } 512 T[] result = (T[])Array.newInstance(kind, length - 1); 513 System.arraycopy(array, 0, result, 0, i); 514 System.arraycopy(array, i + 1, result, i, length - i - 1); 515 return result; 516 } 517 } 518 } 519 return array; 520 } 521 522 /** 523 * Adds value to given array. 524 */ appendInt(@ullable int[] cur, int val, boolean allowDuplicates)525 public static @NonNull int[] appendInt(@Nullable int[] cur, int val, 526 boolean allowDuplicates) { 527 if (cur == null) { 528 return new int[] { val }; 529 } 530 final int N = cur.length; 531 if (!allowDuplicates) { 532 for (int i = 0; i < N; i++) { 533 if (cur[i] == val) { 534 return cur; 535 } 536 } 537 } 538 int[] ret = new int[N + 1]; 539 System.arraycopy(cur, 0, ret, 0, N); 540 ret[N] = val; 541 return ret; 542 } 543 544 /** 545 * Adds value to given array if not already present, providing set-like 546 * behavior. 547 */ 548 @UnsupportedAppUsage appendInt(@ullable int[] cur, int val)549 public static @NonNull int[] appendInt(@Nullable int[] cur, int val) { 550 return appendInt(cur, val, false); 551 } 552 553 /** 554 * Removes value from given array if present, providing set-like behavior. 555 */ removeInt(@ullable int[] cur, int val)556 public static @Nullable int[] removeInt(@Nullable int[] cur, int val) { 557 if (cur == null) { 558 return null; 559 } 560 final int N = cur.length; 561 for (int i = 0; i < N; i++) { 562 if (cur[i] == val) { 563 int[] ret = new int[N - 1]; 564 if (i > 0) { 565 System.arraycopy(cur, 0, ret, 0, i); 566 } 567 if (i < (N - 1)) { 568 System.arraycopy(cur, i + 1, ret, i, N - i - 1); 569 } 570 return ret; 571 } 572 } 573 return cur; 574 } 575 576 /** 577 * Removes value from given array if present, providing set-like behavior. 578 */ removeString(@ullable String[] cur, String val)579 public static @Nullable String[] removeString(@Nullable String[] cur, String val) { 580 if (cur == null) { 581 return null; 582 } 583 final int N = cur.length; 584 for (int i = 0; i < N; i++) { 585 if (Objects.equals(cur[i], val)) { 586 String[] ret = new String[N - 1]; 587 if (i > 0) { 588 System.arraycopy(cur, 0, ret, 0, i); 589 } 590 if (i < (N - 1)) { 591 System.arraycopy(cur, i + 1, ret, i, N - i - 1); 592 } 593 return ret; 594 } 595 } 596 return cur; 597 } 598 599 /** 600 * Adds value to given array if not already present, providing set-like 601 * behavior. 602 */ appendLong(@ullable long[] cur, long val, boolean allowDuplicates)603 public static @NonNull long[] appendLong(@Nullable long[] cur, long val, 604 boolean allowDuplicates) { 605 if (cur == null) { 606 return new long[] { val }; 607 } 608 final int N = cur.length; 609 if (!allowDuplicates) { 610 for (int i = 0; i < N; i++) { 611 if (cur[i] == val) { 612 return cur; 613 } 614 } 615 } 616 long[] ret = new long[N + 1]; 617 System.arraycopy(cur, 0, ret, 0, N); 618 ret[N] = val; 619 return ret; 620 } 621 622 /** 623 * Adds value to given array if not already present, providing set-like 624 * behavior. 625 */ appendBoolean(@ullable boolean[] cur, boolean val)626 public static boolean[] appendBoolean(@Nullable boolean[] cur, boolean val) { 627 if (cur == null) { 628 return new boolean[] { val }; 629 } 630 final int N = cur.length; 631 boolean[] ret = new boolean[N + 1]; 632 System.arraycopy(cur, 0, ret, 0, N); 633 ret[N] = val; 634 return ret; 635 } 636 637 /** 638 * Adds value to given array if not already present, providing set-like 639 * behavior. 640 */ appendLong(@ullable long[] cur, long val)641 public static @NonNull long[] appendLong(@Nullable long[] cur, long val) { 642 return appendLong(cur, val, false); 643 } 644 645 /** 646 * Removes value from given array if present, providing set-like behavior. 647 */ removeLong(@ullable long[] cur, long val)648 public static @Nullable long[] removeLong(@Nullable long[] cur, long val) { 649 if (cur == null) { 650 return null; 651 } 652 final int N = cur.length; 653 for (int i = 0; i < N; i++) { 654 if (cur[i] == val) { 655 long[] ret = new long[N - 1]; 656 if (i > 0) { 657 System.arraycopy(cur, 0, ret, 0, i); 658 } 659 if (i < (N - 1)) { 660 System.arraycopy(cur, i + 1, ret, i, N - i - 1); 661 } 662 return ret; 663 } 664 } 665 return cur; 666 } 667 cloneOrNull(@ullable long[] array)668 public static @Nullable long[] cloneOrNull(@Nullable long[] array) { 669 return (array != null) ? array.clone() : null; 670 } 671 672 /** 673 * Clones an array or returns null if the array is null. 674 */ cloneOrNull(@ullable T[] array)675 public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) { 676 return (array != null) ? array.clone() : null; 677 } 678 cloneOrNull(@ullable ArraySet<T> array)679 public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) { 680 return (array != null) ? new ArraySet<T>(array) : null; 681 } 682 add(@ullable ArraySet<T> cur, T val)683 public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) { 684 if (cur == null) { 685 cur = new ArraySet<>(); 686 } 687 cur.add(val); 688 return cur; 689 } 690 691 /** 692 * Similar to {@link Set#addAll(Collection)}}, but with support for set values of {@code null}. 693 */ addAll(@ullable ArraySet<T> cur, @Nullable Collection<T> val)694 public static @NonNull <T> ArraySet<T> addAll(@Nullable ArraySet<T> cur, 695 @Nullable Collection<T> val) { 696 if (cur == null) { 697 cur = new ArraySet<>(); 698 } 699 if (val != null) { 700 cur.addAll(val); 701 } 702 return cur; 703 } 704 remove(@ullable ArraySet<T> cur, T val)705 public static @Nullable <T> ArraySet<T> remove(@Nullable ArraySet<T> cur, T val) { 706 if (cur == null) { 707 return null; 708 } 709 cur.remove(val); 710 if (cur.isEmpty()) { 711 return null; 712 } else { 713 return cur; 714 } 715 } 716 add(@ullable ArrayList<T> cur, T val)717 public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, T val) { 718 if (cur == null) { 719 cur = new ArrayList<>(); 720 } 721 cur.add(val); 722 return cur; 723 } 724 add(@ullable ArrayList<T> cur, int index, T val)725 public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, int index, T val) { 726 if (cur == null) { 727 cur = new ArrayList<>(); 728 } 729 cur.add(index, val); 730 return cur; 731 } 732 remove(@ullable ArrayList<T> cur, T val)733 public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) { 734 if (cur == null) { 735 return null; 736 } 737 cur.remove(val); 738 if (cur.isEmpty()) { 739 return null; 740 } else { 741 return cur; 742 } 743 } 744 contains(@ullable Collection<T> cur, T val)745 public static <T> boolean contains(@Nullable Collection<T> cur, T val) { 746 return (cur != null) ? cur.contains(val) : false; 747 } 748 trimToSize(@ullable T[] array, int size)749 public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) { 750 if (array == null || size == 0) { 751 return null; 752 } else if (array.length == size) { 753 return array; 754 } else { 755 return Arrays.copyOf(array, size); 756 } 757 } 758 759 /** 760 * Returns true if the two ArrayLists are equal with respect to the objects they contain. 761 * The objects must be in the same order and be reference equal (== not .equals()). 762 */ referenceEquals(ArrayList<T> a, ArrayList<T> b)763 public static <T> boolean referenceEquals(ArrayList<T> a, ArrayList<T> b) { 764 if (a == b) { 765 return true; 766 } 767 768 final int sizeA = a.size(); 769 final int sizeB = b.size(); 770 if (a == null || b == null || sizeA != sizeB) { 771 return false; 772 } 773 774 boolean diff = false; 775 for (int i = 0; i < sizeA && !diff; i++) { 776 diff |= a.get(i) != b.get(i); 777 } 778 return !diff; 779 } 780 781 /** 782 * Removes elements that match the predicate in an efficient way that alters the order of 783 * elements in the collection. This should only be used if order is not important. 784 * @param collection The ArrayList from which to remove elements. 785 * @param predicate The predicate that each element is tested against. 786 * @return the number of elements removed. 787 */ unstableRemoveIf(@ullable ArrayList<T> collection, @NonNull java.util.function.Predicate<T> predicate)788 public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection, 789 @NonNull java.util.function.Predicate<T> predicate) { 790 if (collection == null) { 791 return 0; 792 } 793 794 final int size = collection.size(); 795 int leftIdx = 0; 796 int rightIdx = size - 1; 797 while (leftIdx <= rightIdx) { 798 // Find the next element to remove moving left to right. 799 while (leftIdx < size && !predicate.test(collection.get(leftIdx))) { 800 leftIdx++; 801 } 802 803 // Find the next element to keep moving right to left. 804 while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) { 805 rightIdx--; 806 } 807 808 if (leftIdx >= rightIdx) { 809 // Done. 810 break; 811 } 812 813 Collections.swap(collection, leftIdx, rightIdx); 814 leftIdx++; 815 rightIdx--; 816 } 817 818 // leftIdx is now at the end. 819 for (int i = size - 1; i >= leftIdx; i--) { 820 collection.remove(i); 821 } 822 return size - leftIdx; 823 } 824 defeatNullable(@ullable int[] val)825 public static @NonNull int[] defeatNullable(@Nullable int[] val) { 826 return (val != null) ? val : EmptyArray.INT; 827 } 828 defeatNullable(@ullable String[] val)829 public static @NonNull String[] defeatNullable(@Nullable String[] val) { 830 return (val != null) ? val : EmptyArray.STRING; 831 } 832 defeatNullable(@ullable File[] val)833 public static @NonNull File[] defeatNullable(@Nullable File[] val) { 834 return (val != null) ? val : EMPTY_FILE; 835 } 836 837 /** 838 * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds. 839 * 840 * @param len length of the array. Must be non-negative 841 * @param index the index to check 842 * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array 843 */ checkBounds(int len, int index)844 public static void checkBounds(int len, int index) { 845 if (index < 0 || len <= index) { 846 throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index); 847 } 848 } 849 850 /** 851 * Throws {@link ArrayIndexOutOfBoundsException} if the range is out of bounds. 852 * @param len length of the array. Must be non-negative 853 * @param offset start index of the range. Must be non-negative 854 * @param count length of the range. Must be non-negative 855 * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length 856 * {@code count} is out of bounds of the array 857 */ throwsIfOutOfBounds(int len, int offset, int count)858 public static void throwsIfOutOfBounds(int len, int offset, int count) { 859 if (len < 0) { 860 throw new ArrayIndexOutOfBoundsException("Negative length: " + len); 861 } 862 863 if ((offset | count) < 0 || offset > len - count) { 864 throw new ArrayIndexOutOfBoundsException( 865 "length=" + len + "; regionStart=" + offset + "; regionLength=" + count); 866 } 867 } 868 869 /** 870 * Returns an array with values from {@code val} minus {@code null} values 871 * 872 * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new} 873 */ filterNotNull(T[] val, IntFunction<T[]> arrayConstructor)874 public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) { 875 int nullCount = 0; 876 int size = size(val); 877 for (int i = 0; i < size; i++) { 878 if (val[i] == null) { 879 nullCount++; 880 } 881 } 882 if (nullCount == 0) { 883 return val; 884 } 885 T[] result = arrayConstructor.apply(size - nullCount); 886 int outIdx = 0; 887 for (int i = 0; i < size; i++) { 888 if (val[i] != null) { 889 result[outIdx++] = val[i]; 890 } 891 } 892 return result; 893 } 894 895 /** 896 * Returns an array containing elements from the given one that match the given predicate. 897 * The returned array may, in some cases, be the reference to the input array. 898 */ filter(@ullable T[] items, @NonNull IntFunction<T[]> arrayConstructor, @NonNull java.util.function.Predicate<T> predicate)899 public static @Nullable <T> T[] filter(@Nullable T[] items, 900 @NonNull IntFunction<T[]> arrayConstructor, 901 @NonNull java.util.function.Predicate<T> predicate) { 902 if (isEmpty(items)) { 903 return items; 904 } 905 906 int matchesCount = 0; 907 int size = size(items); 908 final boolean[] tests = new boolean[size]; 909 for (int i = 0; i < size; i++) { 910 tests[i] = predicate.test(items[i]); 911 if (tests[i]) { 912 matchesCount++; 913 } 914 } 915 if (matchesCount == items.length) { 916 return items; 917 } 918 T[] result = arrayConstructor.apply(matchesCount); 919 if (matchesCount == 0) { 920 return result; 921 } 922 int outIdx = 0; 923 for (int i = 0; i < size; i++) { 924 if (tests[i]) { 925 result[outIdx++] = items[i]; 926 } 927 } 928 return result; 929 } 930 startsWith(byte[] cur, byte[] val)931 public static boolean startsWith(byte[] cur, byte[] val) { 932 if (cur == null || val == null) return false; 933 if (cur.length < val.length) return false; 934 for (int i = 0; i < val.length; i++) { 935 if (cur[i] != val[i]) return false; 936 } 937 return true; 938 } 939 940 /** 941 * Returns the first element from the array for which 942 * condition {@code predicate} is true, or null if there is no such element 943 */ find(@ullable T[] items, @NonNull java.util.function.Predicate<T> predicate)944 public static @Nullable <T> T find(@Nullable T[] items, 945 @NonNull java.util.function.Predicate<T> predicate) { 946 if (isEmpty(items)) return null; 947 for (final T item : items) { 948 if (predicate.test(item)) return item; 949 } 950 return null; 951 } 952 deepToString(Object value)953 public static String deepToString(Object value) { 954 if (value != null && value.getClass().isArray()) { 955 if (value.getClass() == boolean[].class) { 956 return Arrays.toString((boolean[]) value); 957 } else if (value.getClass() == byte[].class) { 958 return Arrays.toString((byte[]) value); 959 } else if (value.getClass() == char[].class) { 960 return Arrays.toString((char[]) value); 961 } else if (value.getClass() == double[].class) { 962 return Arrays.toString((double[]) value); 963 } else if (value.getClass() == float[].class) { 964 return Arrays.toString((float[]) value); 965 } else if (value.getClass() == int[].class) { 966 return Arrays.toString((int[]) value); 967 } else if (value.getClass() == long[].class) { 968 return Arrays.toString((long[]) value); 969 } else if (value.getClass() == short[].class) { 970 return Arrays.toString((short[]) value); 971 } else { 972 return Arrays.deepToString((Object[]) value); 973 } 974 } else { 975 return String.valueOf(value); 976 } 977 } 978 979 /** 980 * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code 981 * null}, otherwise returns {@code null}. 982 */ 983 @Nullable getOrNull(@ullable T[] items, int i)984 public static <T> T getOrNull(@Nullable T[] items, int i) { 985 return (items != null && items.length > i) ? items[i] : null; 986 } 987 firstOrNull(T[] items)988 public static @Nullable <T> T firstOrNull(T[] items) { 989 return items.length > 0 ? items[0] : null; 990 } 991 992 /** 993 * Creates a {@link List} from an array. Different from {@link Arrays#asList(Object[])} as that 994 * will use the parameter as the backing array, meaning changes are not isolated. 995 */ toList(T[] array)996 public static <T> List<T> toList(T[] array) { 997 List<T> list = new ArrayList<>(array.length); 998 //noinspection ManualArrayToCollectionCopy 999 for (T item : array) { 1000 //noinspection UseBulkOperation 1001 list.add(item); 1002 } 1003 return list; 1004 } 1005 } 1006