/* * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package xyz.zhouxy.plusone.commons.util; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.Nullable; /** * ArrayTools * *

* 数组工具类 *

* * @author ZhouXY * @since 1.0.0 */ public class ArrayTools { // #region - empty arrays public static final char[] EMPTY_CHAR_ARRAY = {}; public static final byte[] EMPTY_BYTE_ARRAY = {}; public static final short[] EMPTY_SHORT_ARRAY = {}; public static final int[] EMPTY_INT_ARRAY = {}; public static final long[] EMPTY_LONG_ARRAY = {}; public static final float[] EMPTY_FLOAT_ARRAY = {}; public static final double[] EMPTY_DOUBLE_ARRAY = {}; // #endregion public static final int NOT_FOUND_INDEX = -1; // #region - isNullOrEmpty // isNullOrEmpty /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @param 数组中元素的类型 * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable T[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - char /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable char[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - byte /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable byte[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - short /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable short[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - int /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable int[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - long /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable long[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - float /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable float[] arr) { return arr == null || arr.length == 0; } // isNullOrEmpty - double /** * 检查给定数组是否为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组为 {@code null} 或长度为 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNullOrEmpty(@Nullable double[] arr) { return arr == null || arr.length == 0; } // #endregion // #region - isNotEmpty // isNotEmpty /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @param 数组中元素的类型 * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable T[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - char /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable char[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - byte /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable byte[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - short /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable short[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - int /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable int[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - long /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable long[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - float /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable float[] arr) { return arr != null && arr.length > 0; } // isNotEmpty - double /** * 检查给定数组是否不为空 * * @param arr 待检查的数组,可以为 {@code null} * @return 如果数组不为 {@code null} 且长度大于 0,则返回 {@code true};否则返回 {@code false} */ public static boolean isNotEmpty(@Nullable double[] arr) { return arr != null && arr.length > 0; } // #endregion // #region - isAllElementsNotNull /** * 判断数组的所有元素是否都不为空: *
    *
  1. 数组为 {@code null}:抛出异常,因为传入 {@code null} 通常被视为编程错误。 *
  2. 数组不为 {@code null} 但长度为 0:在这种情况下,通常认为数组中的所有元素都不为 {@code null},因此返回 {@code null}。 *
  3. 数组中有至少一个元素为 {@code null}:返回 {@code false}。 *
  4. 数组中所有元素都不为 {@code null}:返回 {@code true}。 *
* * @param 数组元素的类型 * @param arr 待检查的数组。不为 {@code null} * @return 如果数组的所有元素都不为 {@code null},则返回 {@code true};否则返回 {@code false} * * @throws IllegalArgumentException 当参数为空时抛出 */ public static boolean isAllElementsNotNull(final T[] arr) { AssertTools.checkArgument(arr != null, "The array cannot be null."); return Arrays.stream(arr).allMatch(Objects::nonNull); } // #endregion // #region - concat /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static char[] concatCharArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new char[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final char[] result = new char[length]; int i = 0; for (char[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static byte[] concatByteArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new byte[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final byte[] result = new byte[length]; int i = 0; for (byte[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static short[] concatShortArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new short[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final short[] result = new short[length]; int i = 0; for (short[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static int[] concatIntArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new int[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final int[] result = new int[length]; int i = 0; for (int[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static long[] concatLongArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new long[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final long[] result = new long[length]; int i = 0; for (long[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static float[] concatFloatArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new float[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final float[] result = new float[length]; int i = 0; for (float[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 拼接多个数组 * * @param arrays 数组集合,可以为 {@code null} * @return 拼接后的数组 */ public static double[] concatDoubleArray(@Nullable final Collection arrays) { if (arrays == null || arrays.isEmpty()) { return new double[0]; } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final double[] result = new double[length]; int i = 0; for (double[] arr : arraysToConcat) { System.arraycopy(arr, 0, result, i, arr.length); i += arr.length; } return result; } /** * 将集合中的数组连接为一个列表 * * @param arrays 可能包含多个数组的集合,这些数组中的元素将被连接到一个列表中 * @param 泛型参数,表示数组的元素类型 * @return 返回连接后的列表,如果输入的集合为空或包含空数组,则返回空列表 */ public static List concatToList(@Nullable final Collection arrays) { // 如果输入的集合是否为空,则直接返回一个空列表 if (arrays == null || arrays.isEmpty()) { return Collections.emptyList(); } final Collection arraysToConcat = arrays .stream() .filter(Objects::nonNull) .collect(Collectors.toList()); // 计算所有数组的总长度,用于初始化列表的容量 final int length = arraysToConcat.stream().mapToInt(a -> a.length).sum(); final List result = new ArrayList<>(length); for (T[] arr : arraysToConcat) { Collections.addAll(result, arr); } // 返回连接后的列表 return result; } // #endregion // #region - repeat // repeat - char public static char[] repeat(char[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static char[] repeat(char[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_CHAR_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final char[] result = new char[length]; fill(result, 0, length, arr); return result; } // repeat - byte public static byte[] repeat(byte[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static byte[] repeat(byte[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_BYTE_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final byte[] result = new byte[length]; fill(result, 0, length, arr); return result; } // repeat - short public static short[] repeat(short[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static short[] repeat(short[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_SHORT_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final short[] result = new short[length]; fill(result, 0, length, arr); return result; } // repeat - int public static int[] repeat(int[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static int[] repeat(int[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_INT_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final int[] result = new int[length]; fill(result, 0, length, arr); return result; } // repeat - long public static long[] repeat(long[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static long[] repeat(long[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_LONG_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final long[] result = new long[length]; fill(result, 0, length, arr); return result; } // repeat - float public static float[] repeat(float[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static float[] repeat(float[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_FLOAT_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final float[] result = new float[length]; fill(result, 0, length, arr); return result; } // repeat - double public static double[] repeat(double[] arr, int times) { return repeat(arr, times, Integer.MAX_VALUE); } public static double[] repeat(double[] arr, int times, int maxLength) { AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(times >= 0, "The number of times must be greater than or equal to zero"); AssertTools.checkArgument(maxLength >= 0, "The max length must be greater than or equal to zero"); if (times == 0) { return EMPTY_DOUBLE_ARRAY; } final int length = Integer.min(arr.length * times, maxLength); final double[] result = new double[length]; fill(result, 0, length, arr); return result; } // #endregion // #region - fill // fill - char public static void fill(char[] a, @Nullable char[] values) { fill(a, 0, a.length, values); } public static void fill(char[] a, @Nullable String values) { fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY); } public static void fill(char[] a, int fromIndex, int toIndex, @Nullable char[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - byte public static void fill(byte[] a, @Nullable byte[] values) { fill(a, 0, a.length, values); } public static void fill(byte[] a, int fromIndex, int toIndex, @Nullable byte[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - short public static void fill(short[] a, @Nullable short[] values) { fill(a, 0, a.length, values); } public static void fill(short[] a, int fromIndex, int toIndex, @Nullable short[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - int public static void fill(int[] a, @Nullable int[] values) { fill(a, 0, a.length, values); } public static void fill(int[] a, int fromIndex, int toIndex, @Nullable int[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - long public static void fill(long[] a, @Nullable long[] values) { fill(a, 0, a.length, values); } public static void fill(long[] a, int fromIndex, int toIndex, @Nullable long[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - float public static void fill(float[] a, @Nullable float[] values) { fill(a, 0, a.length, values); } public static void fill(float[] a, int fromIndex, int toIndex, @Nullable float[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - double public static void fill(double[] a, @Nullable double[] values) { fill(a, 0, a.length, values); } public static void fill(double[] a, int fromIndex, int toIndex, @Nullable double[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // fill - T public static void fill(T[] a, @Nullable T[] values) { fillInternal(a, 0, a.length, values); } public static void fill(T[] a, int fromIndex, int toIndex, @Nullable T[] values) { fillInternal(a, fromIndex, toIndex, values); } private static void fillInternal(T[] a, int fromIndex, int toIndex, @Nullable T[] values) { AssertTools.checkArgument(Objects.nonNull(a)); if (values == null || values.length == 0) { return; } final int start = Integer.max(fromIndex, 0); final int end = Integer.min(toIndex, a.length); if (start >= end) { return; } for (int i = start; i < end; i += values.length) { for (int j = 0; j < values.length; j++) { final int k = (i + j); if (k < end) { a[k] = values[j]; } else { break; } } } } // #endregion // #region - indexOf public static int indexOfWithPredicate(@Nullable T[] arr, Predicate predicate) { AssertTools.checkNotNull(predicate); if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (predicate.test(arr[i])) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable T[] arr, @Nullable T obj) { return indexOfWithPredicate(arr, item -> Objects.equals(item, obj)); } public static int indexOf(@Nullable char[] arr, char value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable byte[] arr, byte value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable short[] arr, short value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable int[] arr, int value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable long[] arr, long value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable float[] arr, float value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } public static int indexOf(@Nullable double[] arr, double value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = 0; i < arr.length; i++) { if (arr[i] == value) { return i; } } return NOT_FOUND_INDEX; } // #endregion // #region - lastIndexOf public static int lastIndexOfWithPredicate(@Nullable T[] arr, Predicate predicate) { AssertTools.checkNotNull(predicate); if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (predicate.test(arr[i])) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(T[] arr, T obj) { return lastIndexOfWithPredicate(arr, item -> Objects.equals(item, obj)); } public static int lastIndexOf(@Nullable char[] arr, char value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable byte[] arr, byte value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable short[] arr, short value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable int[] arr, int value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable long[] arr, long value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable float[] arr, float value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } public static int lastIndexOf(@Nullable double[] arr, double value) { if (arr == null || arr.length == 0) { return NOT_FOUND_INDEX; } for (int i = arr.length - 1; i >= 0; i--) { if (value == arr[i]) { return i; } } return NOT_FOUND_INDEX; } // #endregion // #region - contains public static boolean contains(@Nullable T[] arr, @Nullable T obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable char[] arr, char obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable byte[] arr, byte obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable short[] arr, short obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable int[] arr, int obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable long[] arr, long obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable float[] arr, float obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean contains(@Nullable double[] arr, double obj) { return indexOf(arr, obj) > NOT_FOUND_INDEX; } public static boolean containsValue(@Nullable BigDecimal[] arr, @Nullable BigDecimal obj) { return indexOfWithPredicate(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX; } // #endregion // #region - private constructor private ArrayTools() { throw new IllegalStateException("Utility class"); } // #endregion }