diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/array/ArrayUtil.java b/hutool-core/src/main/java/cn/hutool/v7/core/array/ArrayUtil.java
index f53207dbc5..b50301258b 100644
--- a/hutool-core/src/main/java/cn/hutool/v7/core/array/ArrayUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/v7/core/array/ArrayUtil.java
@@ -1015,7 +1015,7 @@ public class ArrayUtil extends PrimitiveArrayUtil {
}
// endregion
- // region ----- filter
+ // region ----- edit and filter
/**
* 对每个数组元素执行指定操作,返回操作后的元素
diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/array/PrimitiveArrayUtil.java b/hutool-core/src/main/java/cn/hutool/v7/core/array/PrimitiveArrayUtil.java
index c125ebe55a..501597f996 100644
--- a/hutool-core/src/main/java/cn/hutool/v7/core/array/PrimitiveArrayUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/v7/core/array/PrimitiveArrayUtil.java
@@ -22,8 +22,9 @@ import cn.hutool.v7.core.util.ObjUtil;
import cn.hutool.v7.core.util.RandomUtil;
import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.Random;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
/**
* 原始类型数组工具类,原始类型数据包括:
@@ -1737,6 +1738,7 @@ public class PrimitiveArrayUtil {
return result;
}
+ // endregion
// region ----- removeEle
@@ -1861,6 +1863,568 @@ public class PrimitiveArrayUtil {
}
// endregion
+ // region ----- removeElements
+
+ /**
+ * 从字符数组中移除指定的多个字符
+ *
+ * @param array 原始字符数组
+ * @param elements 要移除的字符数组
+ * @return 移除指定字符后的新数组
+ * @since 7.0.0
+ */
+ public static char[] removeElements(final char[] array, final char... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !ArrayUtil.contains(elements, t));
+ }
+
+ // ==================== byte 类型 ====================
+
+ /**
+ * 从byte数组中移除指定的多个byte元素
+ *
+ * @param array 原始byte数组
+ * @param elements 要移除的byte数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static byte[] removeElements(final byte[] array, final byte... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ // ==================== short 类型 ====================
+
+ /**
+ * 从short数组中移除指定的多个short元素
+ *
+ * @param array 原始short数组
+ * @param elements 要移除的short数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static short[] removeElements(final short[] array, final short... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ /**
+ * 从int数组中移除指定的多个int元素
+ *
+ * @param array 原始int数组
+ * @param elements 要移除的int数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static int[] removeElements(final int[] array, final int... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ /**
+ * 从long数组中移除指定的多个long元素
+ *
+ * @param array 原始long数组
+ * @param elements 要移除的long数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static long[] removeElements(final long[] array, final long... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ /**
+ * 从float数组中移除指定的多个float元素
+ *
+ * @param array 原始float数组
+ * @param elements 要移除的float数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static float[] removeElements(final float[] array, final float... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ /**
+ * 从double数组中移除指定的多个double元素
+ *
+ * @param array 原始double数组
+ * @param elements 要移除的double数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static double[] removeElements(final double[] array, final double... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+
+ /**
+ * 从boolean数组中移除指定的多个boolean元素
+ *
+ * @param array 原始boolean数组
+ * @param elements 要移除的boolean数组
+ * @return 移除指定元素后的新数组
+ * @since 7.0.0
+ */
+ public static boolean[] removeElements(final boolean[] array, final boolean... elements) {
+ if (isEmpty(array) || isEmpty(elements)) {
+ return array;
+ }
+
+ return filter(array, t -> !contains(elements, t));
+ }
+ // endregion
+
+ // region ----- edit and filter
+
+ /**
+ * 对每个数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static char[] edit(final char[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Character modified;
+ for (final Character t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为char[]数组
+ final int size = resultList.size();
+ final char[] resultArray = new char[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个byte数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static byte[] edit(final byte[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Byte modified;
+ for (final Byte t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为byte[]数组
+ final int size = resultList.size();
+ final byte[] resultArray = new byte[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个short数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static short[] edit(final short[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Short modified;
+ for (final Short t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为short[]数组
+ final int size = resultList.size();
+ final short[] resultArray = new short[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个int数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static int[] edit(final int[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Integer modified;
+ for (final Integer t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为int[]数组
+ final int size = resultList.size();
+ final int[] resultArray = new int[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个long数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static long[] edit(final long[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Long modified;
+ for (final Long t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为long[]数组
+ final int size = resultList.size();
+ final long[] resultArray = new long[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个float数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static float[] edit(final float[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Float modified;
+ for (final Float t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为float[]数组
+ final int size = resultList.size();
+ final float[] resultArray = new float[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个double数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static double[] edit(final double[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Double modified;
+ for (final Double t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为double[]数组
+ final int size = resultList.size();
+ final double[] resultArray = new double[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 对每个boolean数组元素执行指定操作,返回操作后的元素
+ * 这个Editor实现可以实现以下功能:
+ *
+ * - 过滤出需要的对象,如果返回{@code null}则抛弃这个元素对象
+ * - 修改元素对象,返回修改后的对象
+ *
+ *
+ * @param array 数组
+ * @param editor 编辑器接口,为 {@code null}则返回原数组
+ * @return 编辑后的数组
+ * @since 7.0.0
+ */
+ public static boolean[] edit(final boolean[] array, final UnaryOperator editor) {
+ if (null == array || null == editor) {
+ return array;
+ }
+
+ final List resultList = new ArrayList<>(array.length);
+ Boolean modified;
+ for (final Boolean t : array) {
+ modified = editor.apply(t);
+ if (null != modified) {
+ resultList.add(modified);
+ }
+ }
+
+ // 将List转换为boolean[]数组
+ final int size = resultList.size();
+ final boolean[] resultArray = new boolean[size];
+ for (int i = 0; i < size; i++) {
+ resultArray[i] = resultList.get(i);
+ }
+
+ return resultArray;
+ }
+
+ /**
+ * 过滤数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static char[] filter(final char[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤byte数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static byte[] filter(final byte[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤short数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static short[] filter(final short[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤int数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static int[] filter(final int[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤long数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static long[] filter(final long[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤float数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static float[] filter(final float[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤double数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static double[] filter(final double[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+
+ /**
+ * 过滤boolean数组元素
+ * 保留 {@link Predicate#test(Object)}为{@code true}的元素
+ *
+ * @param array 数组
+ * @param predicate 过滤器接口,用于定义过滤规则,为{@code null}则返回原数组
+ * @return 过滤后的数组
+ * @since 7.0.0
+ */
+ public static boolean[] filter(final boolean[] array, final Predicate predicate) {
+ if (null == array || null == predicate) {
+ return array;
+ }
+ return edit(array, t -> predicate.test(t) ? t : null);
+ }
+ // endregion
+
// region ----- reverse
/**
@@ -2136,7 +2700,7 @@ public class PrimitiveArrayUtil {
}
// endregion
- // region ------------------------------------------- min and max
+ // region ----- min and max
/**
* 取最小值
diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/lang/Validator.java b/hutool-core/src/main/java/cn/hutool/v7/core/lang/Validator.java
index c4e47a2546..00f15ac4a0 100644
--- a/hutool-core/src/main/java/cn/hutool/v7/core/lang/Validator.java
+++ b/hutool-core/src/main/java/cn/hutool/v7/core/lang/Validator.java
@@ -512,6 +512,19 @@ public class Validator {
return NumberUtil.isNumber(value);
}
+ /**
+ * 辅助方法:检查字符串是否只包含数字
+ *
+ * @param str 字符串
+ * @return 是否只包含数字
+ */
+ public static boolean isNumeric(final String str) {
+ if (StrUtil.isBlank( str)) {
+ return false;
+ }
+ return isMatchRegex("\\d+", str);
+ }
+
/**
* 是否包含数字
*
@@ -621,14 +634,15 @@ public class Validator {
/**
* 验证是否为可用邮箱地址
- * 邮箱地址限制长度为254个字符,参考:https://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address/44317754
+ * 邮箱地址限制长度为254个字符,参考:
+ * what-is-the-maximum-length-of-a-valid-email-address
*
* @param value 值
* @return true为可用邮箱地址
*/
public static boolean isEmail(final CharSequence value) {
final int codeLength = StrUtil.codeLength(value);
- if(codeLength < 1 || codeLength > 254){
+ if (codeLength < 1 || codeLength > 254) {
return false;
}
@@ -1056,9 +1070,9 @@ public class Validator {
// final double doubleValue = value.doubleValue();
// return (doubleValue >= min.doubleValue()) && (doubleValue <= max.doubleValue());
// 通过 NumberUtil 转换为 BigDecimal,使用 BigDecimal 进行比较以保留精度
- BigDecimal valBd = NumberUtil.toBigDecimal(value);
- BigDecimal minBd = NumberUtil.toBigDecimal(min);
- BigDecimal maxBd = NumberUtil.toBigDecimal(max);
+ final BigDecimal valBd = NumberUtil.toBigDecimal(value);
+ final BigDecimal minBd = NumberUtil.toBigDecimal(min);
+ final BigDecimal maxBd = NumberUtil.toBigDecimal(max);
return valBd.compareTo(minBd) >= 0 && valBd.compareTo(maxBd) <= 0;
}
diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/text/StrUtil.java b/hutool-core/src/main/java/cn/hutool/v7/core/text/StrUtil.java
index e8102292f2..bbdee2b25d 100644
--- a/hutool-core/src/main/java/cn/hutool/v7/core/text/StrUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/v7/core/text/StrUtil.java
@@ -128,6 +128,19 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
// region ----- str
+ /**
+ * 将char[]转为String
+ *
+ * @param chars 字符数组
+ * @return 字符串, 如果给定值为null,返回null
+ */
+ public static String str(final char[] chars) {
+ if (null == chars) {
+ return null;
+ }
+ return new String(chars);
+ }
+
/**
* 将对象转为字符串
*
@@ -295,20 +308,21 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
* 该方法按Unicode code point进行反转,支持Unicode字符的正确反转
* 确保复杂字符不会被拆分,如表情符号等多字节字符
*
+ *
* @param str 被反转的字符串
* @return 反转后的字符串,如果输入为null则返回null
* @since 5.8.43
*/
- public static String reverseByCodePoint(String str) {
+ public static String reverseByCodePoint(final String str) {
if (null == str) {
return null;
}
//按Unicode code point方式进行反转处理
- StringBuilder result = new StringBuilder();
+ final StringBuilder result = new StringBuilder();
for (int i = str.length(); i > 0; ) {
//获取指定位置前的code point
- int codePoint = str.codePointBefore(i);
+ final int codePoint = str.codePointBefore(i);
//根据code point的字符数量调整索引位置
i -= Character.charCount(codePoint);
//将code point追加到结果中
diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/util/RandomUtil.java b/hutool-core/src/main/java/cn/hutool/v7/core/util/RandomUtil.java
index 971cf0c677..3aca2dde56 100644
--- a/hutool-core/src/main/java/cn/hutool/v7/core/util/RandomUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/v7/core/util/RandomUtil.java
@@ -27,10 +27,12 @@ import java.util.concurrent.ThreadLocalRandom;
*/
public class RandomUtil {
+ // region ----- static Strings
/**
* 用于随机选的数字
*/
public static final String NUMBERS = "0123456789";
+
/**
* 用于随机选的大写字符
*/
@@ -51,6 +53,35 @@ public class RandomUtil {
* 用于随机选的字符和数字(包括大写和小写字母)
*/
public static final String LETTERS_NUMBERS = LETTERS + NUMBERS;
+ // endregion
+
+ // region ----- static chars
+ /**
+ * 用于随机选的数字
+ */
+ private static final char[] NUMBERS_CHARS = NUMBERS.toCharArray();
+
+ /**
+ * 用于随机选的大写字符
+ */
+ private static final char[] LETTERS_UPPER_CHARS = LETTERS_UPPER.toCharArray();
+ /**
+ * 用于随机选的小写字符
+ */
+ private static final char[] LETTERS_LOWER_CHARS = LETTERS_LOWER.toCharArray();
+ /**
+ * 用于随机选的字符(包含大写和小写)
+ */
+ private static final char[] LETTERS_CHARS = LETTERS.toCharArray();
+ /**
+ * 用于随机选的字符和数字(小写)
+ */
+ private static final char[] LETTERS_NUMBERS_LOWER_CHARS = LETTERS_NUMBERS_LOWER.toCharArray();
+ /**
+ * 用于随机选的字符和数字(包括大写和小写字母)
+ */
+ private static final char[] LETTERS_NUMBERS_CHARS = LETTERS_NUMBERS.toCharArray();
+ // endregion
// region ----- get or create Random
@@ -274,7 +305,7 @@ public class RandomUtil {
* @since 5.2.1
*/
public static int[] randomInts(final int length) {
- final int[] range = NumberUtil.range(length);
+ final int[] range = NumberUtil.range(length - 1);
for (int i = 0; i < length; i++) {
final int random = randomInt(i, length);
ArrayUtil.swap(range, i, random);
@@ -648,7 +679,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomLettersAndNumbers(final int length) {
- return randomString(LETTERS_NUMBERS, length);
+ return randomString(LETTERS_NUMBERS_CHARS, length);
}
/**
@@ -658,7 +689,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomLettersAndNumbersLower(final int length) {
- return randomString(LETTERS_NUMBERS_LOWER, length);
+ return randomString(LETTERS_NUMBERS_LOWER_CHARS, length);
}
/**
@@ -669,34 +700,38 @@ public class RandomUtil {
* @since 4.0.13
*/
public static String randomLettersAndNumbersUpper(final int length) {
- return randomString(LETTERS_NUMBERS_LOWER, length).toUpperCase();
+ return randomString(LETTERS_NUMBERS_LOWER_CHARS, length).toUpperCase();
}
/**
* 获得一个随机的字符串(只包含数字和字母) 并排除指定字符串
*
* @param length 字符串的长度
- * @param elemData 要排除的字符串,如:去重容易混淆的字符串,oO0、lL1、q9Q、pP,区分大小写
+ * @param elemData 要排除的字符串,如:去重容易混淆的字符串,oO0、lL1、q9Q、pP,区分大小写
* @return 随机字符串
*/
public static String randomLettersAndNumbersWithoutStr(final int length, final String elemData) {
- String baseStr = LETTERS_NUMBERS;
- baseStr = StrUtil.removeAll(baseStr, elemData.toCharArray());
- return randomString(baseStr, length);
+ char[] baseChars = LETTERS_NUMBERS_CHARS;
+ if(StrUtil.isNotBlank(elemData)){
+ baseChars = ArrayUtil.removeElements(baseChars, elemData.toCharArray());
+ }
+ return randomString(baseChars, length);
}
/**
* 获得一个随机的字符串(只包含数字和小写字母) 并排除指定字符串
*
* @param length 字符串的长度
- * @param elemData 要排除的字符串,如:去重容易混淆的字符串,oO0、lL1、q9Q、pP,不区分大小写
+ * @param elemData 要排除的字符串,如:去重容易混淆的字符串,oO0、lL1、q9Q、pP,不区分大小写
* @return 随机字符串
* @since 5.8.28
*/
public static String randomLettersAndNumbersLowerWithoutStr(final int length, final String elemData) {
- String baseStr = LETTERS_NUMBERS_LOWER;
- baseStr = StrUtil.removeAll(baseStr, elemData.toLowerCase().toCharArray());
- return randomString(baseStr, length);
+ char[] baseChars = LETTERS_NUMBERS_LOWER_CHARS;
+ if(StrUtil.isNotBlank(elemData)){
+ baseChars = ArrayUtil.removeElements(baseChars, elemData.toLowerCase().toCharArray());
+ }
+ return randomString(baseChars, length);
}
/**
@@ -706,7 +741,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomNumbers(final int length) {
- return randomString(NUMBERS, length);
+ return randomString(NUMBERS_CHARS, length);
}
/**
@@ -716,7 +751,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomLetters(final int length) {
- return randomString(LETTERS, length);
+ return randomString(LETTERS_CHARS, length);
}
/**
@@ -726,7 +761,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomLettersLower(final int length) {
- return randomString(LETTERS_LOWER, length);
+ return randomString(LETTERS_LOWER_CHARS, length);
}
/**
@@ -736,7 +771,7 @@ public class RandomUtil {
* @return 随机字符串
*/
public static String randomLettersUpper(final int length) {
- return randomString(LETTERS_UPPER, length);
+ return randomString(LETTERS_UPPER_CHARS, length);
}
/**
@@ -746,21 +781,20 @@ public class RandomUtil {
* @param length 字符串的长度
* @return 随机字符串
*/
- public static String randomString(final String baseString, int length) {
- if (StrUtil.isEmpty(baseString)) {
- return StrUtil.EMPTY;
- }
- if (length < 1) {
- length = 1;
- }
+ public static String randomString(final String baseString, final int length) {
+ Assert.notEmpty(baseString, "Base string can not be empty !");
+ return randomString(baseString.toCharArray(), length);
+ }
- final StringBuilder sb = new StringBuilder(length);
- final int baseLength = baseString.length();
- for (int i = 0; i < length; i++) {
- final int number = randomInt(baseLength);
- sb.append(baseString.charAt(number));
- }
- return sb.toString();
+ /**
+ * 获得一个随机的字符串
+ *
+ * @param baseString 随机字符选取的样本
+ * @param length 字符串的长度
+ * @return 随机字符串
+ */
+ public static String randomString(final char[] baseString, final int length) {
+ return new String(randomChars(baseString, length));
}
// endregion
@@ -773,7 +807,7 @@ public class RandomUtil {
* @since 3.1.2
*/
public static char randomNumber() {
- return randomChar(NUMBERS);
+ return randomChar(NUMBERS_CHARS);
}
/**
@@ -783,7 +817,7 @@ public class RandomUtil {
* @since 3.1.2
*/
public static char randomChar() {
- return randomChar(LETTERS_NUMBERS_LOWER);
+ return randomChar(LETTERS_NUMBERS_LOWER_CHARS);
}
/**
@@ -796,6 +830,41 @@ public class RandomUtil {
public static char randomChar(final String baseString) {
return baseString.charAt(randomInt(baseString.length()));
}
+
+ /**
+ * 随机字符
+ *
+ * @param baseChars 随机字符选取的样本chars
+ * @return 随机字符
+ * @since 7.0.0
+ */
+ public static char randomChar(final char[] baseChars) {
+ return baseChars[randomInt(baseChars.length)];
+ }
+
+ /**
+ * 获得一个随机的字符串
+ *
+ * @param baseChars 随机字符选取的样本
+ * @param length 字符串的长度
+ * @return 随机字符串
+ */
+ public static char[] randomChars(final char[] baseChars, final int length) {
+ if(ArrayUtil.isEmpty(baseChars)){
+ throw new IllegalArgumentException("baseChars can not be empty !");
+ }
+ Assert.isTrue(length >= 1, "Length can not less than 1 !");
+
+ // 预先将基础字符串转为字符数组,避免每次charAt调用
+ final int baseLength = baseChars.length;
+ final char[] result = new char[length];
+
+ for (int i = 0; i < length; i++) {
+ result[i] = baseChars[randomInt(baseLength)];
+ }
+
+ return result;
+ }
// endregion
// region ----- weightRandom
@@ -857,4 +926,4 @@ public class RandomUtil {
return DateUtil.offset(baseDate, dateField, randomInt(min, max));
}
// endregion
-}
\ No newline at end of file
+}
diff --git a/hutool-core/src/test/java/cn/hutool/v7/core/array/PrimitiveArrayUtilTest.java b/hutool-core/src/test/java/cn/hutool/v7/core/array/PrimitiveArrayUtilTest.java
new file mode 100644
index 0000000000..601086d7d6
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/v7/core/array/PrimitiveArrayUtilTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2026 Hutool Team.
+ *
+ * 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
+ *
+ * http://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 cn.hutool.v7.core.array;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class PrimitiveArrayUtilTest {
+ /**
+ * 测试当原始数组为null时的情况
+ */
+ @Test
+ public void testRemoveElementsWithNullArray() {
+ final char[] result = PrimitiveArrayUtil.removeElements((char[]) null, 'a', 'b');
+ assertNull(result, "当原始数组为null时,应返回null");
+ }
+
+ /**
+ * 测试当原始数组为空数组时的情况
+ */
+ @Test
+ public void testRemoveElementsWithEmptyArray() {
+ final char[] original = {};
+ final char[] elementsToRemove = {'a', 'b'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(original, result, "当原始数组为空数组时,应返回原数组");
+ }
+
+ /**
+ * 测试当要移除的元素数组为null时的情况
+ */
+ @Test
+ public void testRemoveElementsWithNullElements() {
+ final char[] original = {'a', 'b', 'c'};
+ final char[] result = PrimitiveArrayUtil.removeElements(original, (char[]) null);
+
+ assertArrayEquals(original, result, "当要移除的元素数组为null时,应返回原数组");
+ }
+
+ /**
+ * 测试当要移除的元素数组为空时的情况
+ */
+ @Test
+ public void testRemoveElementsWithEmptyElements() {
+ final char[] original = {'a', 'b', 'c'};
+ final char[] elementsToRemove = {};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(original, result, "当要移除的元素数组为空时,应返回原数组");
+ }
+
+ /**
+ * 测试移除存在的字符元素
+ */
+ @Test
+ public void testRemoveElementsExistingChars() {
+ final char[] original = {'a', 'b', 'c', 'd', 'e'};
+ final char[] elementsToRemove = {'b', 'd'};
+ final char[] expected = {'a', 'c', 'e'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "应正确移除指定的字符元素");
+ }
+
+ /**
+ * 测试移除不存在的字符元素
+ */
+ @Test
+ public void testRemoveElementsNonExistingChars() {
+ final char[] original = {'a', 'b', 'c'};
+ final char[] elementsToRemove = {'x', 'y'};
+ final char[] expected = {'a', 'b', 'c'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "当要移除的字符不存在时,应返回原数组");
+ }
+
+ /**
+ * 测试移除所有元素的情况
+ */
+ @Test
+ public void testRemoveElementsAllChars() {
+ final char[] original = {'a', 'b', 'c'};
+ final char[] elementsToRemove = {'a', 'b', 'c'};
+ final char[] expected = {};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "当移除所有元素时,应返回空数组");
+ }
+
+ /**
+ * 测试包含重复字符的情况
+ */
+ @Test
+ public void testRemoveElementsWithDuplicates() {
+ final char[] original = {'a', 'b', 'a', 'c', 'b', 'd'};
+ final char[] elementsToRemove = {'a', 'b'};
+ final char[] expected = {'c', 'd'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "应正确处理重复字符的移除");
+ }
+
+ /**
+ * 测试包含特殊字符的情况
+ */
+ @Test
+ public void testRemoveElementsWithSpecialChars() {
+ final char[] original = {' ', 'a', '\n', 'b', '\t', 'c'};
+ final char[] elementsToRemove = {' ', '\n'};
+ final char[] expected = {'a', 'b', '\t', 'c'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "应正确处理特殊字符的移除");
+ }
+
+ /**
+ * 测试移除单个字符的情况
+ */
+ @Test
+ public void testRemoveElementsSingleChar() {
+ final char[] original = {'a', 'b', 'c', 'd'};
+ final char[] elementsToRemove = {'c'};
+ final char[] expected = {'a', 'b', 'd'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "应正确移除单个字符");
+ }
+
+ /**
+ * 测试移除一个不存在的单个字符
+ */
+ @Test
+ public void testRemoveElementsSingleNonExistingChar() {
+ final char[] original = {'a', 'b', 'c'};
+ final char[] elementsToRemove = {'x'};
+ final char[] expected = {'a', 'b', 'c'};
+
+ final char[] result = PrimitiveArrayUtil.removeElements(original, elementsToRemove);
+
+ assertArrayEquals(expected, result, "当移除不存在的单个字符时,应返回原数组");
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/v7/core/util/RandomUtilTest.java b/hutool-core/src/test/java/cn/hutool/v7/core/util/RandomUtilTest.java
index b4100e2aed..1683ba9832 100644
--- a/hutool-core/src/test/java/cn/hutool/v7/core/util/RandomUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/v7/core/util/RandomUtilTest.java
@@ -18,17 +18,15 @@ package cn.hutool.v7.core.util;
import cn.hutool.v7.core.collection.ListUtil;
import cn.hutool.v7.core.convert.ConvertUtil;
-import cn.hutool.v7.core.lang.Console;
+import cn.hutool.v7.core.lang.Validator;
import cn.hutool.v7.core.math.NumberUtil;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.math.RoundingMode;
import java.util.List;
import java.util.Locale;
import java.util.Set;
-import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.*;
@@ -52,12 +50,6 @@ public class RandomUtilTest {
assertTrue(randomDouble <= 1);
}
- @Test
- @Disabled
- public void randomBooleanTest() {
- Console.log(RandomUtil.randomBoolean());
- }
-
@Test
public void randomNumberTest() {
final char c = RandomUtil.randomNumber();
@@ -94,8 +86,8 @@ public class RandomUtilTest {
@Test
public void randomStringOfLengthTest(){
- final String s = RandomUtil.randomString("123", -1);
- assertNotNull(s);
+ assertThrows(IllegalArgumentException.class, () -> RandomUtil.randomString("123", 0));
+ assertThrows(IllegalArgumentException.class, () -> RandomUtil.randomString("123", -1));
}
@Test
@@ -114,27 +106,17 @@ public class RandomUtilTest {
// 测试生成长度为5的数字字符串
final String result1 = RandomUtil.randomNumbers(5);
assertEquals(5, result1.length(), "生成的字符串长度应该等于指定长度");
- assertTrue(isNumeric(result1), "生成的字符串应该只包含数字");
+ assertTrue(Validator.isNumeric(result1), "生成的字符串应该只包含数字");
// 测试生成长度为10的数字字符串
final String result2 = RandomUtil.randomNumbers(10);
assertEquals(10, result2.length(), "生成的字符串长度应该等于指定长度");
- assertTrue(isNumeric(result2), "生成的字符串应该只包含数字");
+ assertTrue(Validator.isNumeric(result2), "生成的字符串应该只包含数字");
// 测试生成长度为1的数字字符串
final String result3 = RandomUtil.randomNumbers(1);
assertEquals(1, result3.length(), "生成的字符串长度应该等于指定长度");
- assertTrue(isNumeric(result3), "生成的字符串应该只包含数字");
-
- // 测试生成长度为0的数字字符串(会被调整为1)
- final String result4 = RandomUtil.randomNumbers(0);
- assertEquals(1, result4.length(), "当长度为0时,应生成长度为1的字符串");
- assertTrue(isNumeric(result4), "生成的字符串应该只包含数字");
-
- // 测试生成长度为负数的数字字符串(会被调整为1)
- final String result5 = RandomUtil.randomNumbers(-5);
- assertEquals(1, result5.length(), "当长度为负数时,应生成长度为1的字符串");
- assertTrue(isNumeric(result5), "生成的字符串应该只包含数字");
+ assertTrue(Validator.isNumeric(result3), "生成的字符串应该只包含数字");
// 验证多次生成的结果都不相同(概率性验证)
final String result6 = RandomUtil.randomNumbers(8);
@@ -142,19 +124,121 @@ public class RandomUtilTest {
// 由于是随机生成,不能保证一定不同,但大部分情况下应该不同
// 所以我们主要验证它们都符合预期格式
assertEquals(8, result6.length(), "生成的字符串长度应该等于指定长度");
- assertTrue(isNumeric(result6), "生成的字符串应该只包含数字");
+ assertTrue(Validator.isNumeric(result6), "生成的字符串应该只包含数字");
assertEquals(8, result7.length(), "生成的字符串长度应该等于指定长度");
- assertTrue(isNumeric(result7), "生成的字符串应该只包含数字");
+ assertTrue(Validator.isNumeric(result7), "生成的字符串应该只包含数字");
}
/**
- * 辅助方法:检查字符串是否只包含数字
+ * 测试 randomDouble(double limit, int scale, RoundingMode roundingMode) 方法正常功能
+ * 验证生成的随机数在正确范围内,并且精度符合要求
*/
- private boolean isNumeric(final String str) {
- if (str == null || str.isEmpty()) {
- return false;
+ @Test
+ public void testRandomDoubleWithScaleAndRoundingMode() {
+ // 测试基本功能:生成0到10之间的随机数,保留2位小数,四舍五入
+ final double result = RandomUtil.randomDouble(10.0, 2, RoundingMode.HALF_UP);
+
+ // 验证结果在[0, 10)范围内
+ assertTrue(result >= 0.0 && result < 10.0,
+ "随机数应该在[0, 10)范围内,实际值:" + result);
+
+ // 验证小数点后最多2位数字
+ final String resultStr = String.valueOf(result);
+ if (resultStr.contains(".")) {
+ final int decimalPlaces = resultStr.length() - resultStr.indexOf('.') - 1;
+ assertTrue(decimalPlaces <= 2,
+ "小数位数应该不超过2位,实际:" + decimalPlaces);
+ }
+ }
+
+ /**
+ * 测试不同的舍入模式
+ */
+ @Test
+ public void testRandomDoubleWithDifferentRoundingModes() {
+ final double limit = 5.0;
+
+ // 测试向上舍入
+ final double upResult = RandomUtil.randomDouble(limit, 2, RoundingMode.UP);
+ assertTrue(upResult >= 0.0 && upResult < limit,
+ "UP模式下随机数应该在[0, 5)范围内");
+
+ // 测试向下舍入
+ final double downResult = RandomUtil.randomDouble(limit, 2, RoundingMode.DOWN);
+ assertTrue(downResult >= 0.0 && downResult < limit,
+ "DOWN模式下随机数应该在[0, 5)范围内");
+
+ // 测试四舍五入
+ final double halfUpResult = RandomUtil.randomDouble(limit, 2, RoundingMode.HALF_UP);
+ assertTrue(halfUpResult >= 0.0 && halfUpResult < limit,
+ "HALF_UP模式下随机数应该在[0, 5)范围内");
+ }
+
+ /**
+ * 测试精度为0的情况
+ */
+ @Test
+ public void testRandomDoubleWithZeroScale() {
+ final double result = RandomUtil.randomDouble(10.0, 0, RoundingMode.HALF_UP);
+
+ // 验证结果在[0, 10)范围内
+ assertTrue(result >= 0.0 && result < 10.0,
+ "随机数应该在[0, 10)范围内");
+
+ // 验证没有小数部分(整数)
+ assertEquals(Math.floor(result), result, 0.001,
+ "精度为0时应该返回整数");
+ }
+
+ /**
+ * 测试舍入模式为null的情况(应使用默认的HALF_UP模式)
+ */
+ @Test
+ public void testRandomDoubleWithNullRoundingMode() {
+ final double result = RandomUtil.randomDouble(10.0, 2, null);
+
+ // 验证结果在[0, 10)范围内
+ assertTrue(result >= 0.0 && result < 10.0,
+ "随机数应该在[0, 10)范围内");
+
+ // 验证小数位数不超过2位
+ final String resultStr = String.valueOf(result);
+ if (resultStr.contains(".")) {
+ final int decimalPlaces = resultStr.length() - resultStr.indexOf('.') - 1;
+ assertTrue(decimalPlaces <= 2,
+ "小数位数应该不超过2位,实际:" + decimalPlaces);
+ }
+ }
+
+ /**
+ * 测试边界情况:limit为较小值
+ */
+ @Test
+ public void testRandomDoubleWithSmallLimit() {
+ final double result = RandomUtil.randomDouble(0.001, 5, RoundingMode.HALF_UP);
+
+ // 验证结果在[0, 0.001)范围内
+ assertTrue(result >= 0.0 && result < 0.001,
+ "随机数应该在[0, 0.001)范围内,实际值:" + result);
+ }
+
+ /**
+ * 测试较大精度的情况
+ */
+ @Test
+ public void testRandomDoubleWithHighPrecision() {
+ final double result = RandomUtil.randomDouble(100.0, 10, RoundingMode.HALF_UP);
+
+ // 验证结果在[0, 100)范围内
+ assertTrue(result >= 0.0 && result < 100.0,
+ "随机数应该在[0, 100)范围内");
+
+ // 验证结果经过了精度处理
+ final String resultStr = String.valueOf(result);
+ if (resultStr.contains(".")) {
+ final int decimalPlaces = resultStr.length() - resultStr.indexOf('.') - 1;
+ assertTrue(decimalPlaces <= 10,
+ "小数位数应该不超过10位,实际:" + decimalPlaces);
}
- final Pattern pattern = Pattern.compile("\\d+");
- return pattern.matcher(str).matches();
}
}
diff --git a/hutool-core/src/test/java/cn/hutool/v7/core/util/TestRandomUtilByDoubao.java b/hutool-core/src/test/java/cn/hutool/v7/core/util/TestRandomUtilByDoubao.java
new file mode 100644
index 0000000000..37d8b8de8f
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/v7/core/util/TestRandomUtilByDoubao.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2026 Hutool Team.
+ *
+ * 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
+ *
+ * http://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 cn.hutool.v7.core.util;
+
+import cn.hutool.v7.core.date.DateField;
+import cn.hutool.v7.core.date.DateTime;
+import cn.hutool.v7.core.date.DateUnit;
+import cn.hutool.v7.core.date.DateUtil;
+import cn.hutool.v7.core.lang.selector.WeightObj;
+import cn.hutool.v7.core.lang.selector.WeightRandomSelector;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.security.SecureRandom;
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * RandomUtil 单元测试类
+ * 基于 JUnit 5 实现,覆盖所有核心方法的功能验证
+ */
+public class TestRandomUtilByDoubao {
+
+ // ===================== 基础随机数生成测试 =====================
+ @Test
+ void testGetRandom() {
+ // 测试普通随机数生成器
+ final ThreadLocalRandom random = RandomUtil.getRandom();
+ assertNotNull(random);
+
+ // 测试安全随机数生成器
+ final Random secureRandom = RandomUtil.getRandom(true);
+ assertInstanceOf(SecureRandom.class, secureRandom);
+
+ // 测试非安全随机数生成器
+ final Random normalRandom = RandomUtil.getRandom(false);
+ assertInstanceOf(ThreadLocalRandom.class, normalRandom);
+ }
+
+ @Test
+ void testRandomBoolean() {
+ // 验证生成的布尔值在多次调用中会出现 true 和 false
+ boolean hasTrue = false;
+ boolean hasFalse = false;
+ for (int i = 0; i < 100; i++) {
+ final boolean result = RandomUtil.randomBoolean();
+ if (result) {
+ hasTrue = true;
+ } else {
+ hasFalse = true;
+ }
+ if (hasTrue && hasFalse) {
+ break;
+ }
+ }
+ assertTrue(hasTrue && hasFalse);
+ }
+
+ @Test
+ void testRandomBytes() {
+ // 测试随机字节数组长度
+ final int length = 10;
+ final byte[] bytes = RandomUtil.randomBytes(length);
+ assertEquals(length, bytes.length);
+
+ // 测试指定随机数生成器的字节数组
+ final byte[] bytesWithRandom = RandomUtil.randomBytes(length, new Random());
+ assertEquals(length, bytesWithRandom.length);
+ }
+
+ @SuppressWarnings("UnnecessaryUnicodeEscape")
+ @Test
+ void testRandomChinese() {
+ // 验证生成的字符是汉字(Unicode 范围:\u4E00 - \u9FFF)
+ final char chineseChar = RandomUtil.randomChinese();
+ assertTrue(chineseChar >= '\u4E00' && chineseChar <= '\u9FFF');
+ }
+
+ // ===================== 整数随机数测试 =====================
+ @SuppressWarnings("ConstantValue")
+ @Test
+ void testRandomInt() {
+ // 测试无参随机整数
+ final int randomInt = RandomUtil.randomInt();
+ assertTrue(randomInt >= Integer.MIN_VALUE && randomInt <= Integer.MAX_VALUE);
+
+ // 测试 [0, limit) 范围
+ final int limit = 100;
+ final int intWithLimit = RandomUtil.randomInt(limit);
+ assertTrue(intWithLimit >= 0 && intWithLimit < limit);
+
+ // 测试 [min, max) 范围
+ final int min = 10;
+ final int max = 20;
+ final int intInRange = RandomUtil.randomInt(min, max);
+ assertTrue(intInRange >= min && intInRange < max);
+
+ // 测试包含最大值的情况
+ final int intWithMax = RandomUtil.randomInt(min, max, true, true);
+ assertTrue(intWithMax >= min && intWithMax <= max);
+
+ // 测试不包含最小值的情况
+ final int intWithoutMin = RandomUtil.randomInt(min, max, false, false);
+ assertTrue(intWithoutMin > min && intWithoutMin < max);
+
+ // 测试随机索引数组
+ final int[] randomInts = RandomUtil.randomInts(5);
+ assertEquals(5, randomInts.length);
+ // 验证索引是 0-4 的乱序排列
+ final Set indexSet = new HashSet<>();
+ for (final int num : randomInts) {
+ indexSet.add(num);
+ assertTrue(num >= 0 && num < 5);
+ }
+ assertEquals(5, indexSet.size());
+ }
+
+ // ===================== 长整数随机数测试 =====================
+ @SuppressWarnings("ConstantValue")
+ @Test
+ void testRandomLong() {
+ // 测试无参随机长整数
+ final long randomLong = RandomUtil.randomLong();
+ assertTrue(randomLong >= Long.MIN_VALUE && randomLong <= Long.MAX_VALUE);
+
+ // 测试 [0, limit) 范围
+ final long limit = 1000L;
+ final long longWithLimit = RandomUtil.randomLong(limit);
+ assertTrue(longWithLimit >= 0 && longWithLimit < limit);
+
+ // 测试 [min, max) 范围
+ final long min = 100L;
+ final long max = 200L;
+ final long longInRange = RandomUtil.randomLong(min, max);
+ assertTrue(longInRange >= min && longInRange < max);
+
+ // 测试包含最大值的情况
+ final long longWithMax = RandomUtil.randomLong(min, max, true, true);
+ assertTrue(longWithMax >= min && longWithMax <= max);
+ }
+
+ // ===================== 浮点数随机数测试 =====================
+ @Test
+ void testRandomFloat() {
+ // 测试 [0, 1) 范围
+ final float float01 = RandomUtil.randomFloat();
+ assertTrue(float01 >= 0 && float01 < 1);
+
+ // 测试 [0, limit) 范围
+ final float limit = 100.0f;
+ final float floatWithLimit = RandomUtil.randomFloat(limit);
+ assertTrue(floatWithLimit >= 0 && floatWithLimit < limit);
+
+ // 测试 [min, max) 范围
+ final float min = 10.0f;
+ final float max = 20.0f;
+ final float floatInRange = RandomUtil.randomFloat(min, max);
+ assertTrue(floatInRange >= min && floatInRange < max);
+
+ // 测试最小值等于最大值的情况
+ final float floatEqual = RandomUtil.randomFloat(min, min);
+ assertEquals(min, floatEqual);
+ }
+
+ // ===================== 双精度浮点数随机数测试 =====================
+ @Test
+ void testRandomDouble() {
+ // 测试 [0, 1) 范围
+ final double double01 = RandomUtil.randomDouble();
+ assertTrue(double01 >= 0 && double01 < 1);
+
+ // 测试 [0, limit) 范围
+ final double limit = 1000.0;
+ final double doubleWithLimit = RandomUtil.randomDouble(limit);
+ assertTrue(doubleWithLimit >= 0 && doubleWithLimit < limit);
+
+ // 测试 [min, max) 范围
+ final double min = 100.0;
+ final double max = 200.0;
+ final double doubleInRange = RandomUtil.randomDouble(min, max);
+ assertTrue(doubleInRange >= min && doubleInRange < max);
+
+ // 测试保留小数位数
+ final double doubleWithScale = RandomUtil.randomDouble(min, max, 2, RoundingMode.HALF_UP);
+ // 验证小数位数不超过 2 位
+ final String doubleStr = String.valueOf(doubleWithScale);
+ int dotIndex = doubleStr.indexOf('.');
+ if (dotIndex != -1) {
+ assertTrue(doubleStr.length() - dotIndex - 1 <= 2);
+ }
+
+ // 测试无范围保留小数
+ final double doubleScaleOnly = RandomUtil.randomDouble(3, RoundingMode.HALF_UP);
+ final String scaleStr = String.valueOf(doubleScaleOnly);
+ dotIndex = scaleStr.indexOf('.');
+ if (dotIndex != -1) {
+ assertTrue(scaleStr.length() - dotIndex - 1 <= 3);
+ }
+ }
+
+ // ===================== 高精度小数随机数测试 =====================
+ @Test
+ void testRandomBigDecimal() {
+ // 测试 [0, 1) 范围
+ final BigDecimal bd01 = RandomUtil.randomBigDecimal();
+ assertTrue(bd01.compareTo(BigDecimal.ZERO) >= 0 && bd01.compareTo(BigDecimal.ONE) < 0);
+
+ // 测试 [0, limit) 范围
+ final BigDecimal limit = new BigDecimal("100.0");
+ final BigDecimal bdWithLimit = RandomUtil.randomBigDecimal(limit);
+ assertTrue(bdWithLimit.compareTo(BigDecimal.ZERO) >= 0 && bdWithLimit.compareTo(limit) < 0);
+
+ // 测试 [min, max) 范围
+ final BigDecimal min = new BigDecimal("10.0");
+ final BigDecimal max = new BigDecimal("20.0");
+ final BigDecimal bdInRange = RandomUtil.randomBigDecimal(min, max);
+ assertTrue(bdInRange.compareTo(min) >= 0 && bdInRange.compareTo(max) < 0);
+ }
+
+ // ===================== 随机元素选取测试 =====================
+ @Test
+ void testRandomEle() {
+ // 测试列表随机元素
+ final List list = Arrays.asList("A", "B", "C", "D");
+ final String randomEle = RandomUtil.randomEle(list);
+ assertTrue(list.contains(randomEle));
+
+ // 测试列表前 N 项随机元素
+ final String randomEleLimit = RandomUtil.randomEle(list, 2);
+ assertTrue(Arrays.asList("A", "B").contains(randomEleLimit));
+
+ // 测试数组随机元素
+ final String[] array = {"X", "Y", "Z"};
+ final String randomArrayEle = RandomUtil.randomEle(array);
+ assertTrue(Arrays.asList(array).contains(randomArrayEle));
+
+ // 测试数组前 N 项随机元素
+ final String randomArrayEleLimit = RandomUtil.randomEle(array, 2);
+ assertTrue(Arrays.asList("X", "Y").contains(randomArrayEleLimit));
+ }
+
+ @Test
+ void testRandomEles() {
+ // 测试随机获取多个元素(允许重复)
+ final List list = Arrays.asList(1, 2, 3, 4, 5);
+ final List randomEles = RandomUtil.randomEles(list, 3);
+ assertEquals(3, randomEles.size());
+ for (final Integer num : randomEles) {
+ assertTrue(list.contains(num));
+ }
+ }
+
+ @Test
+ void testRandomPick() {
+ // 测试随机选取不重复位置的元素
+ final List list = Arrays.asList("a", "b", "c", "d", "e");
+ final List picked = RandomUtil.randomPick(list, 3);
+ assertEquals(3, picked.size());
+ // 验证选取的元素都是原列表中的
+ for (final String s : picked) {
+ assertTrue(list.contains(s));
+ }
+
+ // 测试选取数量大于等于列表长度的情况
+ final List pickedAll = RandomUtil.randomPick(list, 10);
+ assertEquals(list.size(), pickedAll.size());
+ assertEquals(new HashSet<>(list), new HashSet<>(pickedAll));
+ }
+
+ @Test
+ void testRandomPickInts() {
+ // 测试随机选取整数数组元素
+ final int[] seed = {1, 2, 3, 4, 5};
+ final int[] pickedInts = RandomUtil.randomPickInts(3, seed.clone());
+ assertEquals(3, pickedInts.length);
+ // 验证选取的元素都是原数组中的
+ final Set seedSet = new HashSet<>();
+ for (final int num : seed) {
+ seedSet.add(num);
+ }
+ for (final int num : pickedInts) {
+ assertTrue(seedSet.contains(num));
+ }
+
+ // 测试选取数量超过种子长度的异常
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ RandomUtil.randomPickInts(10, seed);
+ });
+ }
+
+ @Test
+ void testRandomEleSet() {
+ // 测试随机获取不重复元素集合
+ final List list = Arrays.asList("1", "2", "3", "4", "5", "1", "2");
+ final Set randomSet = RandomUtil.randomEleSet(list, 3);
+ assertEquals(3, randomSet.size());
+ // 验证元素都是原列表中的去重元素
+ final Set distinctSet = new HashSet<>(list);
+ for (final String s : randomSet) {
+ assertTrue(distinctSet.contains(s));
+ }
+
+ // 测试选取数量超过去重后长度的异常
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ RandomUtil.randomEleSet(list, 10);
+ });
+ }
+
+ // ===================== 随机字符串生成测试 =====================
+ @Test
+ void testRandomString() {
+ final int length = 8;
+
+ // 测试数字+大小写字母
+ final String lettersAndNumbers = RandomUtil.randomLettersAndNumbers(length);
+ assertEquals(length, lettersAndNumbers.length());
+ assertTrue(lettersAndNumbers.matches("[0-9A-Za-z]+"));
+
+ // 测试数字+小写字母
+ final String lower = RandomUtil.randomLettersAndNumbersLower(length);
+ assertEquals(length, lower.length());
+ assertTrue(lower.matches("[0-9a-z]+"));
+
+ // 测试数字+大写字母
+ final String upper = RandomUtil.randomLettersAndNumbersUpper(length);
+ assertEquals(length, upper.length());
+ assertTrue(upper.matches("[0-9A-Z]+"));
+
+ // 测试排除指定字符
+ final String withoutStr = RandomUtil.randomLettersAndNumbersWithoutStr(length, "0OlL1");
+ assertEquals(length, withoutStr.length());
+ Assertions.assertFalse(withoutStr.matches(".*[0OlL1].*"));
+
+ // 测试小写字母+数字排除指定字符
+ final String lowerWithout = RandomUtil.randomLettersAndNumbersLowerWithoutStr(length, "0OlL1");
+ assertEquals(length, lowerWithout.length());
+ Assertions.assertFalse(lowerWithout.matches(".*[0ol1].*"));
+
+ // 测试纯数字
+ final String numbers = RandomUtil.randomNumbers(length);
+ assertEquals(length, numbers.length());
+ assertTrue(numbers.matches("[0-9]+"));
+
+ // 测试纯字母
+ final String letters = RandomUtil.randomLetters(length);
+ assertEquals(length, letters.length());
+ assertTrue(letters.matches("[A-Za-z]+"));
+
+ // 测试纯小写字母
+ final String lettersLower = RandomUtil.randomLettersLower(length);
+ assertEquals(length, lettersLower.length());
+ assertTrue(lettersLower.matches("[a-z]+"));
+
+ // 测试纯大写字母
+ final String lettersUpper = RandomUtil.randomLettersUpper(length);
+ assertEquals(length, lettersUpper.length());
+ assertTrue(lettersUpper.matches("[A-Z]+"));
+
+ // 测试自定义字符集
+ final String custom = RandomUtil.randomString("abc123", length);
+ assertEquals(length, custom.length());
+ assertTrue(custom.matches("[abc123]+"));
+
+ // 测试空字符集异常
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ RandomUtil.randomString("", length);
+ });
+ }
+
+ // ===================== 随机字符生成测试 =====================
+ @Test
+ void testRandomChar() {
+ // 测试随机数字字符
+ final char numberChar = RandomUtil.randomNumber();
+ assertTrue(Character.isDigit(numberChar));
+
+ // 测试随机小写字母+数字字符
+ final char charLower = RandomUtil.randomChar();
+ assertTrue(Character.isDigit(charLower) || (charLower >= 'a' && charLower <= 'z'));
+
+ // 测试自定义字符集字符
+ final char customChar = RandomUtil.randomChar("ABC123");
+ assertTrue("ABC123".indexOf(customChar) != -1);
+
+ // 测试字符数组随机字符
+ final char[] chars = {'X', 'Y', 'Z'};
+ final char arrayChar = RandomUtil.randomChar(chars);
+ assertTrue(Arrays.asList('X', 'Y', 'Z').contains(arrayChar));
+
+ // 测试随机字符数组生成
+ final char[] randomChars = RandomUtil.randomChars(chars, 5);
+ assertEquals(5, randomChars.length);
+ for (final char c : randomChars) {
+ assertTrue(Arrays.asList('X', 'Y', 'Z').contains(c));
+ }
+ }
+
+ // ===================== 权重随机测试 =====================
+ @SuppressWarnings("unchecked")
+ @Test
+ void testWeightRandom() {
+ // 构建权重对象列表
+ final WeightObj obj1 = new WeightObj<>("A", 1);
+ final WeightObj obj2 = new WeightObj<>("B", 9);
+ final List> weightObjs = Arrays.asList(obj1, obj2);
+
+ // 测试数组形式的权重随机
+ final WeightRandomSelector selector1 = RandomUtil.weightRandom(weightObjs.toArray(new WeightObj[0]));
+ // 测试迭代器形式的权重随机
+ final WeightRandomSelector selector2 = RandomUtil.weightRandom(weightObjs);
+
+ // 验证多次随机后,权重高的元素出现概率更高(概率性验证)
+ int countA = 0;
+ int countB = 0;
+ final int total = 10000;
+ for (int i = 0; i < total; i++) {
+ final String result = selector1.select();
+ if ("A".equals(result)) {
+ countA++;
+ } else if ("B".equals(result)) {
+ countB++;
+ }
+ }
+ // 验证 A 的出现概率约 10%,B 约 90%(允许 ±2% 误差)
+ assertTrue(countA >= 800 && countA <= 1200);
+ assertTrue(countB >= 8800 && countB <= 9200);
+ }
+
+ // ===================== 随机日期测试 =====================
+ @Test
+ void testRandomDate() {
+ // 测试基于当天的随机天数
+ final DateTime randomDay = RandomUtil.randomDay(-5, 5);
+ // 验证日期在 [-5, 4] 天范围内(因为 max 不包含)
+ final long diff = DateUtil.betweenDay(DateUtil.now(), randomDay, true);
+ assertTrue(diff >= -5 && diff <= 4);
+
+ // 测试基于指定日期的随机时间
+ final DateTime baseDate = DateUtil.parse("2024-01-01 12:00:00");
+ final DateTime randomDate = RandomUtil.randomDate(baseDate, DateField.HOUR_OF_DAY, -2, 3);
+ // 验证小时偏移在 [-2, 2] 范围内
+ final long hourDiff = DateUtil.between(baseDate, randomDate, DateUnit.HOUR);
+ assertTrue(hourDiff >= -2 && hourDiff <= 2);
+
+ // 测试空基准日期
+ final DateTime randomDateWithNull = RandomUtil.randomDate(null, DateField.MINUTE, -10, 10);
+ assertNotNull(randomDateWithNull);
+ }
+}