This commit is contained in:
Looly
2025-12-13 17:29:36 +08:00
parent efe58ca8b7
commit da85577867
5 changed files with 404 additions and 17 deletions

View File

@@ -1941,33 +1941,33 @@ public class ArrayUtil extends PrimitiveArrayUtil {
*
* @param <T> 数组元素类型
* @param array 数组,不允许为空
* @param start 开始位置(包括)
* @param end 结束位置(不包括)
* @param beginInclude 开始位置(包括)
* @param endExclude 结束位置(不包括)
* @return 新的数组
* @see Arrays#copyOfRange(Object[], int, int)
* @since 4.2.2
*/
public static <T> T[] sub(final T[] array, int start, int end) {
public static <T> T[] sub(final T[] array, int beginInclude, int endExclude) {
Assert.notNull(array, "array must be not null !");
final int length = length(array);
if (start < 0) {
start += length;
if (beginInclude < 0) {
beginInclude += length;
}
if (end < 0) {
end += length;
if (endExclude < 0) {
endExclude += length;
}
if (start > end) {
final int tmp = start;
start = end;
end = tmp;
if (beginInclude > endExclude) {
final int tmp = beginInclude;
beginInclude = endExclude;
endExclude = tmp;
}
if (start >= length) {
if (beginInclude >= length) {
return newArray(array.getClass().getComponentType(), 0);
}
if (end > length) {
end = length;
if (endExclude > length) {
endExclude = length;
}
return Arrays.copyOfRange(array, start, end);
return Arrays.copyOfRange(array, beginInclude, endExclude);
}
/**
@@ -2343,4 +2343,6 @@ public class ArrayUtil extends PrimitiveArrayUtil {
}
return true;
}
}

View File

@@ -26,7 +26,17 @@ import java.util.Arrays;
import java.util.Random;
/**
* 原始类型数组工具类
* 原始类型数组工具类,原始类型数据包括:
* <ol>
* <li>{@code int[]}</li>
* <li>{@code long[]}</li>
* <li>{@code double[]}</li>
* <li>{@code float[]}</li>
* <li>{@code short[]}</li>
* <li>{@code char[]}</li>
* <li>{@code byte[]}</li>
* <li>{@code boolean[]}</li>
* </ol>
*
* @author Looly
* @since 5.5.2

View File

@@ -15,7 +15,15 @@
*/
/**
* 提供数组相关封装
* 提供数组相关封装,作为{@link java.util.Arrays}的功能补充。<br>
* 在Java中数组是基本数据结构 用来表示一组固定长度的相同类型的元素集合。<br>
* 由于数组的长度不可变的特性,因此针对数组封装了{@link cn.hutool.v7.core.array.ArrayUtil}提供一些类似于List有的特性。<br>
* 在Java语言中分为原始类型和对象类型的元素因此对应数组操作也区分为原始类型数组和对象类型数组。
* <ul>
* <li>原始类型数组包括类似{@code int[]}、{@code long[]}、{@code double[]}、{@code float[]}、{@code short[]}、
* {@code char[]}、{@code byte[]}、{@code boolean[]}等类型,封装方法在{@link cn.hutool.v7.core.array.PrimitiveArrayUtil}</li>
* <li>对象类型数组可以使用泛型表示,类似于`T[]`,封装于{@link cn.hutool.v7.core.array.ArrayUtil}</li>
* </ul>
*
* @author Looly
*/

View File

@@ -0,0 +1,255 @@
package cn.hutool.v7.core.array;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* ArrayUtil.isSub 和 ArrayUtil.indexOfSub 方法单元测试
*/
class ArrayUtilIsSubTest {
@Test
void testIsSubWithNullArray() {
// 测试空数组
assertFalse(ArrayUtil.isSub(null, new Integer[]{1, 2}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2}, null));
assertFalse(ArrayUtil.isSub(null, null));
}
@Test
void testIsSubWithEmptyArray() {
// 测试空数组
assertFalse(ArrayUtil.isSub(new Integer[]{}, new Integer[]{1, 2}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2}, new Integer[]{}));
assertFalse(ArrayUtil.isSub(new Integer[]{}, new Integer[]{}));
}
@Test
void testIsSubWithExactMatch() {
// 完全匹配的情况
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3}, new Integer[]{1, 2, 3}));
assertTrue(ArrayUtil.isSub(new String[]{"a", "b", "c"}, new String[]{"a", "b", "c"}));
}
@Test
void testIsSubWithSubAtStart() {
// 子数组在开头
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{1, 2}));
assertTrue(ArrayUtil.isSub(new String[]{"a", "b", "c", "d"}, new String[]{"a", "b"}));
}
@Test
void testIsSubWithSubInMiddle() {
// 子数组在中间
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{2, 3}));
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{3, 4}));
assertTrue(ArrayUtil.isSub(new String[]{"a", "b", "c", "d"}, new String[]{"b", "c"}));
}
@Test
void testIsSubWithSubAtEnd() {
// 子数组在结尾
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{4, 5}));
assertTrue(ArrayUtil.isSub(new String[]{"a", "b", "c", "d"}, new String[]{"c", "d"}));
}
@Test
void testIsSubWithSingleElement() {
// 单个元素匹配
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{3}));
assertTrue(ArrayUtil.isSub(new String[]{"a", "b", "c"}, new String[]{"b"}));
}
@Test
void testIsSubWithNoMatch() {
// 不匹配的情况
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{6, 7}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3}, new Integer[]{2, 4}));
assertFalse(ArrayUtil.isSub(new String[]{"a", "b", "c"}, new String[]{"d", "e"}));
}
@Test
void testIsSubWithPartialMatch() {
// 部分匹配但不是完整子数组
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{2, 4}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{1, 3}));
}
@Test
void testIsSubWithLargerSubArray() {
// 子数组比原数组大
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3}, new Integer[]{1, 2, 3, 4}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2}, new Integer[]{1, 2, 3}));
}
@Test
void testIsSubWithDuplicateValues() {
// 包含重复值的情况
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 2, 3, 4}, new Integer[]{2, 2}));
assertTrue(ArrayUtil.isSub(new Integer[]{1, 2, 2, 3, 4}, new Integer[]{2, 3}));
assertFalse(ArrayUtil.isSub(new Integer[]{1, 2, 3, 4}, new Integer[]{2, 2}));
}
@Test
void testIndexOfSubWithNullArray() {
// 测试空数组
assertEquals(-1, ArrayUtil.indexOfSub(null, new Integer[]{1, 2}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2}, null));
assertEquals(-1, ArrayUtil.indexOfSub(null, null));
}
@Test
void testIndexOfSubWithEmptyArray() {
// 测试空数组
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{}, new Integer[]{1, 2}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2}, new Integer[]{}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{}, new Integer[]{}));
}
@Test
void testIndexOfSubWithExactMatch() {
// 完全匹配的情况
assertEquals(0, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3}, new Integer[]{1, 2, 3}));
assertEquals(0, ArrayUtil.indexOfSub(new String[]{"a", "b", "c"}, new String[]{"a", "b", "c"}));
}
@Test
void testIndexOfSubWithSubAtStart() {
// 子数组在开头
assertEquals(0, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{1, 2}));
assertEquals(0, ArrayUtil.indexOfSub(new String[]{"a", "b", "c", "d"}, new String[]{"a", "b"}));
}
@Test
void testIndexOfSubWithSubInMiddle() {
// 子数组在中间
assertEquals(1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{2, 3}));
assertEquals(2, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{3, 4}));
assertEquals(1, ArrayUtil.indexOfSub(new String[]{"a", "b", "c", "d"}, new String[]{"b", "c"}));
}
@Test
void testIndexOfSubWithSubAtEnd() {
// 子数组在结尾
assertEquals(3, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{4, 5}));
assertEquals(2, ArrayUtil.indexOfSub(new String[]{"a", "b", "c", "d"}, new String[]{"c", "d"}));
}
@Test
void testIndexOfSubWithSingleElement() {
// 单个元素匹配
assertEquals(2, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{3}));
assertEquals(1, ArrayUtil.indexOfSub(new String[]{"a", "b", "c"}, new String[]{"b"}));
}
@Test
void testIndexOfSubWithNoMatch() {
// 不匹配的情况
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{6, 7}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3}, new Integer[]{2, 4}));
assertEquals(-1, ArrayUtil.indexOfSub(new String[]{"a", "b", "c"}, new String[]{"d", "e"}));
}
@Test
void testIndexOfSubWithPartialMatch() {
// 部分匹配但不是完整子数组
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{2, 4}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4, 5}, new Integer[]{1, 3}));
}
@Test
void testIndexOfSubWithLargerSubArray() {
// 子数组比原数组大
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3}, new Integer[]{1, 2, 3, 4}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2}, new Integer[]{1, 2, 3}));
}
@Test
void testIndexOfSubWithDuplicateValues() {
// 包含重复值的情况
assertEquals(1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 2, 3, 4}, new Integer[]{2, 2}));
assertEquals(2, ArrayUtil.indexOfSub(new Integer[]{1, 2, 2, 3, 4}, new Integer[]{2, 3}));
assertEquals(-1, ArrayUtil.indexOfSub(new Integer[]{1, 2, 3, 4}, new Integer[]{2, 2}));
}
@Test
void testIndexOfSubWithBeginInclude() {
// 测试带开始位置的indexOfSub方法
final Integer[] array = {1, 2, 3, 4, 5, 2, 3, 6};
// 从开始位置查找
assertEquals(1, ArrayUtil.indexOfSub(array, 0, new Integer[]{2, 3}));
// 从中间位置查找
assertEquals(5, ArrayUtil.indexOfSub(array, 2, new Integer[]{2, 3}));
// 从超过匹配位置查找
assertEquals(-1, ArrayUtil.indexOfSub(array, 6, new Integer[]{2, 3}));
// 负索引转换为正索引
assertEquals(5, ArrayUtil.indexOfSub(array, -3, new Integer[]{2, 3})); // -3 = array.length - 3 = 5
}
@Test
void testIndexOfSubWithBeginIncludeEdgeCases() {
final Integer[] array = {1, 2, 3, 4, 5};
// 开始位置为负数且转换后仍然为负数
assertEquals(-1, ArrayUtil.indexOfSub(array, -10, new Integer[]{1}));
// 开始位置超出数组范围
assertEquals(-1, ArrayUtil.indexOfSub(array, 10, new Integer[]{1}));
// 开始位置等于数组长度
assertEquals(-1, ArrayUtil.indexOfSub(array, 5, new Integer[]{1}));
// 剩余长度不足
assertEquals(-1, ArrayUtil.indexOfSub(array, 3, new Integer[]{2, 3}));
}
@Test
void testIndexOfSubWithBeginIncludeNegativeIndex() {
final Integer[] array = {1, 2, 3, 4, 5, 2, 3, 6};
// 负索引转换为正索引的测试
assertEquals(5, ArrayUtil.indexOfSub(array, -3, new Integer[]{2, 3})); // -3 -> 5
assertEquals(6, ArrayUtil.indexOfSub(array, -2, new Integer[]{3, 6})); // -2 -> 6
assertEquals(-1, ArrayUtil.indexOfSub(array, -1, new Integer[]{2, 3})); // -1 -> 7超出范围
}
@Test
void testIsSubWithDifferentObjectTypes() {
// 测试不同类型的对象
assertTrue(ArrayUtil.isSub(
new Object[]{1, "hello", 3.14, true},
new Object[]{"hello", 3.14}
));
assertFalse(ArrayUtil.isSub(
new Object[]{1, "hello", 3.14, true},
new Object[]{"hello", 3.14, false}
));
}
@Test
void testIsSubWithNullElements() {
// 测试包含null元素的情况
assertTrue(ArrayUtil.isSub(
new String[]{"a", null, "c", "d"},
new String[]{null, "c"}
));
assertFalse(ArrayUtil.isSub(
new String[]{"a", "b", "c", "d"},
new String[]{null, "c"}
));
assertTrue(ArrayUtil.isSub(
new String[]{null, "b", "c"},
new String[]{null}
));
}
}

View File

@@ -856,6 +856,118 @@ public class ArrayUtilTest {
assertArrayEquals(new int[]{2, 3, 4}, ArrayUtil.sub(arr, -4, -1));
}
@Test
public void subGenericTest() {
// 测试 String 数组
final String[] strArray = {"a", "b", "c", "d", "e"};
assertArrayEquals(new String[]{}, ArrayUtil.sub(strArray, 2, 2));
assertArrayEquals(new String[]{}, ArrayUtil.sub(strArray, 5, 5));
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, ArrayUtil.sub(strArray, 0, 5));
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, ArrayUtil.sub(strArray, 5, 0));
assertArrayEquals(new String[]{"a"}, ArrayUtil.sub(strArray, 0, 1));
assertArrayEquals(new String[]{"e"}, ArrayUtil.sub(strArray, 4, 5));
assertArrayEquals(new String[]{"b", "c", "d"}, ArrayUtil.sub(strArray, 1, 4));
assertArrayEquals(new String[]{"b", "c", "d"}, ArrayUtil.sub(strArray, 4, 1));
assertArrayEquals(new String[]{"b", "c", "d"}, ArrayUtil.sub(strArray, 1, -1));
assertArrayEquals(new String[]{"b", "c", "d"}, ArrayUtil.sub(strArray, -1, 1));
assertArrayEquals(new String[]{"b", "c", "d"}, ArrayUtil.sub(strArray, -4, -1));
// 测试 Integer 数组
final Integer[] intArray = {1, 2, 3, 4, 5};
assertArrayEquals(new Integer[]{}, ArrayUtil.sub(intArray, 2, 2));
assertArrayEquals(new Integer[]{}, ArrayUtil.sub(intArray, 5, 5));
assertArrayEquals(new Integer[]{1, 2, 3, 4, 5}, ArrayUtil.sub(intArray, 0, 5));
assertArrayEquals(new Integer[]{1, 2, 3, 4, 5}, ArrayUtil.sub(intArray, 5, 0));
assertArrayEquals(new Integer[]{1}, ArrayUtil.sub(intArray, 0, 1));
assertArrayEquals(new Integer[]{5}, ArrayUtil.sub(intArray, 4, 5));
assertArrayEquals(new Integer[]{2, 3, 4}, ArrayUtil.sub(intArray, 1, 4));
assertArrayEquals(new Integer[]{2, 3, 4}, ArrayUtil.sub(intArray, 4, 1));
assertArrayEquals(new Integer[]{2, 3, 4}, ArrayUtil.sub(intArray, 1, -1));
assertArrayEquals(new Integer[]{2, 3, 4}, ArrayUtil.sub(intArray, -1, 1));
assertArrayEquals(new Integer[]{2, 3, 4}, ArrayUtil.sub(intArray, -4, -1));
// 测试边界情况
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, ArrayUtil.sub(strArray, 0, 10));
assertArrayEquals(new String[]{}, ArrayUtil.sub(strArray, 10, 15));
assertArrayEquals(new String[]{"d", "e"}, ArrayUtil.sub(strArray, 3, 10));
assertArrayEquals(new String[]{"a", "b"}, ArrayUtil.sub(strArray, -5, 2));
assertArrayEquals(new String[]{"d", "e"}, ArrayUtil.sub(strArray, 3, 10));
// 测试负索引转换
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, ArrayUtil.sub(strArray, -5, 5));
assertArrayEquals(new String[]{"e"}, ArrayUtil.sub(strArray, -1, 5));
assertArrayEquals(new String[]{"a"}, ArrayUtil.sub(strArray, -5, -4));
// 测试单元素数组
final String[] singleArray = {"single"};
assertArrayEquals(new String[]{}, ArrayUtil.sub(singleArray, 0, 0));
assertArrayEquals(new String[]{}, ArrayUtil.sub(singleArray, 1, 1));
assertArrayEquals(new String[]{"single"}, ArrayUtil.sub(singleArray, 0, 1));
assertArrayEquals(new String[]{"single"}, ArrayUtil.sub(singleArray, 0, 5));
assertArrayEquals(new String[]{}, ArrayUtil.sub(singleArray, -1, -1));
assertArrayEquals(new String[]{"single"}, ArrayUtil.sub(singleArray, -1, 1));
// 测试空数组
final String[] emptyArray = {};
assertArrayEquals(new String[]{}, ArrayUtil.sub(emptyArray, 0, 0));
assertArrayEquals(new String[]{}, ArrayUtil.sub(emptyArray, 0, 1));
}
@Test
public void subGenericTestForObjectArray() {
// 测试 String 数组
final Object strArray = new String[]{"a", "b", "c", "d", "e"};
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(strArray, 2, 2));
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(strArray, 5, 5));
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, (String[])ArrayUtil.sub(strArray, 0, 5));
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, (String[])ArrayUtil.sub(strArray, 5, 0));
assertArrayEquals(new String[]{"a"}, (String[])ArrayUtil.sub(strArray, 0, 1));
assertArrayEquals(new String[]{"e"}, (String[])ArrayUtil.sub(strArray, 4, 5));
assertArrayEquals(new String[]{"b", "c", "d"}, (String[])ArrayUtil.sub(strArray, 1, 4));
assertArrayEquals(new String[]{"b", "c", "d"}, (String[])ArrayUtil.sub(strArray, 4, 1));
assertArrayEquals(new String[]{"b", "c", "d"}, (String[])ArrayUtil.sub(strArray, 1, -1));
assertArrayEquals(new String[]{"b", "c", "d"}, (String[])ArrayUtil.sub(strArray, -1, 1));
assertArrayEquals(new String[]{"b", "c", "d"}, (String[])ArrayUtil.sub(strArray, -4, -1));
// 测试边界情况
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, (String[])ArrayUtil.sub(strArray, 0, 10));
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(strArray, 10, 15));
assertArrayEquals(new String[]{"d", "e"}, (String[])ArrayUtil.sub(strArray, 3, 10));
assertArrayEquals(new String[]{"a", "b"}, (String[])ArrayUtil.sub(strArray, -5, 2));
assertArrayEquals(new String[]{"d", "e"}, (String[])ArrayUtil.sub(strArray, 3, 10));
// 测试负索引转换
assertArrayEquals(new String[]{"a", "b", "c", "d", "e"}, (String[])ArrayUtil.sub(strArray, -5, 5));
assertArrayEquals(new String[]{"e"}, (String[])ArrayUtil.sub(strArray, -1, 5));
assertArrayEquals(new String[]{"a"}, (String[])ArrayUtil.sub(strArray, -5, -4));
// 测试单元素数组
final Object singleArray = new String[]{"single"};
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(singleArray, 0, 0));
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(singleArray, 1, 1));
assertArrayEquals(new String[]{"single"}, (String[])ArrayUtil.sub(singleArray, 0, 1));
assertArrayEquals(new String[]{"single"}, (String[])ArrayUtil.sub(singleArray, 0, 5));
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(singleArray, -1, -1));
assertArrayEquals(new String[]{"single"}, (String[])ArrayUtil.sub(singleArray, -1, 1));
// 测试空数组
final Object emptyArray = new String[]{};
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(emptyArray, 0, 0));
assertArrayEquals(new String[]{}, (String[])ArrayUtil.sub(emptyArray, 0, 1));
}
@Test
public void subGenericExceptionTest() {
// 测试空指针异常
assertThrows(IllegalArgumentException.class, () -> ArrayUtil.sub((String[]) null, 0, 1));
// 测试正常调用不应抛出异常
final String[] strArray = {"a", "b", "c"};
assertDoesNotThrow(() -> ArrayUtil.sub(strArray, 0, 3));
assertDoesNotThrow(() -> ArrayUtil.sub(strArray, -3, 3));
assertDoesNotThrow(() -> ArrayUtil.sub(strArray, 0, 10));
}
@Test
public void isSortedTest() {
final Integer[] a = {1, 1, 2, 2, 2, 3, 3};