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