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