forked from plusone/plusone-commons
feat: 新增 RandomTools#randomInt 用于生成指定区间内的随机整数 (!2 @Gitee)
This commit is contained in:
@@ -24,10 +24,11 @@ import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import com.google.common.collect.BoundType;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
/**
|
||||
* 随机工具类
|
||||
* <p>
|
||||
* 建议调用方自行维护 Random 对象
|
||||
*
|
||||
* @author ZhouXY108 <luquanlion@outlook.com>
|
||||
*/
|
||||
@@ -46,18 +47,41 @@ public final class RandomTools {
|
||||
DEFAULT_SECURE_RANDOM = secureRandom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 大写字母
|
||||
*/
|
||||
public static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
/**
|
||||
* 小写字母
|
||||
*/
|
||||
public static final String LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
||||
/**
|
||||
* 数字
|
||||
*/
|
||||
public static final String NUMBERS = "0123456789";
|
||||
|
||||
/**
|
||||
* 默认的 {@code SecureRandom}
|
||||
*
|
||||
* @return 默认的 {@code SecureRandom}
|
||||
*/
|
||||
public static SecureRandom defaultSecureRandom() {
|
||||
return DEFAULT_SECURE_RANDOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前线程的 {@code ThreadLocalRandom}
|
||||
*
|
||||
* @return 当前线程的 {@code ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom currentThreadLocalRandom() {
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #region - randomStr
|
||||
// ================================
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成指定长度的字符串
|
||||
*
|
||||
@@ -75,12 +99,26 @@ public final class RandomTools {
|
||||
return randomStrInternal(random, sourceCharacters, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前线程的 {@code ThreadLocalRandom},生成指定长度的字符串
|
||||
*
|
||||
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
|
||||
* @param length 字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String randomStr(char[] sourceCharacters, int length) {
|
||||
checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
|
||||
checkArgument(length >= 0, "The length should be greater than or equal to zero.");
|
||||
return randomStrInternal(ThreadLocalRandom.current(), sourceCharacters, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认的 {@code SecureRandom},生成指定长度的字符串
|
||||
*
|
||||
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
|
||||
* @param length 字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String secureRandomStr(char[] sourceCharacters, int length) {
|
||||
checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
|
||||
checkArgument(length >= 0, "The length should be greater than or equal to zero.");
|
||||
@@ -104,18 +142,131 @@ public final class RandomTools {
|
||||
return randomStrInternal(random, sourceCharacters, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前线程的 {@code ThreadLocalRandom},生成指定长度的字符串
|
||||
*
|
||||
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
|
||||
* @param length 字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String randomStr(String sourceCharacters, int length) {
|
||||
checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
|
||||
checkArgument(length >= 0, "The length should be greater than or equal to zero.");
|
||||
return randomStrInternal(ThreadLocalRandom.current(), sourceCharacters, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认的 {@code SecureRandom},生成指定长度的字符串
|
||||
*
|
||||
* @param sourceCharacters 字符池。字符串的字符将在数组中选,不为空
|
||||
* @param length 字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String secureRandomStr(String sourceCharacters, int length) {
|
||||
checkArgumentNotNull(sourceCharacters, "Source characters cannot be null.");
|
||||
checkArgument(length >= 0, "The length should be greater than or equal to zero.");
|
||||
return randomStrInternal(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - randomStr
|
||||
// ================================
|
||||
|
||||
// ================================
|
||||
// #region - randomInt
|
||||
// ================================
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成随机整数
|
||||
*
|
||||
* @param startInclusive 最小值(包含)
|
||||
* @param endExclusive 最大值(不包含)
|
||||
* @return 在区间 {@code [min, max)} 内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int randomInt(Random random, int startInclusive, int endExclusive) {
|
||||
checkArgumentNotNull(random, "Random cannot be null.");
|
||||
checkArgument(startInclusive < endExclusive, "Start value must be less than end value.");
|
||||
return randomIntInternal(random, startInclusive, endExclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前线程的 {@code ThreadLocalRandom},生成随机整数
|
||||
*
|
||||
* @param startInclusive 最小值(包含)
|
||||
* @param endExclusive 最大值(不包含)
|
||||
* @return 在区间 {@code [min, max)} 内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int randomInt(int startInclusive, int endExclusive) {
|
||||
checkArgument(startInclusive < endExclusive, "Start value must be less than end value.");
|
||||
return randomIntInternal(ThreadLocalRandom.current(), startInclusive, endExclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认的 {@code SecureRandom},生成随机整数
|
||||
*
|
||||
* @param startInclusive 最小值(包含)
|
||||
* @param endExclusive 最大值(不包含)
|
||||
* @return 在区间 {@code [min, max)} 内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int secureRandomInt(int startInclusive, int endExclusive) {
|
||||
checkArgument(startInclusive < endExclusive, "Start value must be less than end value.");
|
||||
return randomIntInternal(DEFAULT_SECURE_RANDOM, startInclusive, endExclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成随机整数
|
||||
*
|
||||
* @param range 整数区间
|
||||
* @return 在指定区间内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int randomInt(Random random, Range<Integer> range) {
|
||||
checkArgumentNotNull(random, "Random cannot be null.");
|
||||
checkArgumentNotNull(range, "Range cannot be null.");
|
||||
return randomIntInternal(random, range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用当前线程的 {@code ThreadLocalRandom},生成随机整数
|
||||
*
|
||||
* @param range 整数区间
|
||||
* @return 在指定区间内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int randomInt(Range<Integer> range) {
|
||||
checkArgumentNotNull(range, "Range cannot be null.");
|
||||
return randomIntInternal(ThreadLocalRandom.current(), range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认的 {@code SecureRandom},生成随机整数
|
||||
*
|
||||
* @param range 整数区间
|
||||
* @return 在指定区间内的随机整数
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static int secureRandomInt(Range<Integer> range) {
|
||||
checkArgumentNotNull(range, "Range cannot be null.");
|
||||
return randomIntInternal(DEFAULT_SECURE_RANDOM, range);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - randomInt
|
||||
// ================================
|
||||
|
||||
// ================================
|
||||
// #region - private methods
|
||||
// ================================
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成指定长度的字符串
|
||||
*
|
||||
@@ -158,6 +309,35 @@ public final class RandomTools {
|
||||
return String.valueOf(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成随机整数
|
||||
*
|
||||
* @param startInclusive 最小值(包含)
|
||||
* @param endExclusive 最大值(不包含)
|
||||
* @return 在区间 {@code [min, max)} 内的随机整数
|
||||
*/
|
||||
private static int randomIntInternal(Random random, int startInclusive, int endExclusive) {
|
||||
return random.nextInt(endExclusive - startInclusive) + startInclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用传入的随机数生成器,生成随机整数
|
||||
*
|
||||
* @param range 整数区间
|
||||
* @return 在指定区间内的随机整数
|
||||
*/
|
||||
private static int randomIntInternal(Random random, Range<Integer> range) {
|
||||
Integer lowerEndpoint = range.lowerEndpoint();
|
||||
Integer upperEndpoint = range.upperEndpoint();
|
||||
int min = range.lowerBoundType() == BoundType.CLOSED ? lowerEndpoint : lowerEndpoint + 1;
|
||||
int max = range.upperBoundType() == BoundType.OPEN ? upperEndpoint : upperEndpoint + 1;
|
||||
return random.nextInt(max - min) + min;
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - private methods
|
||||
// ================================
|
||||
|
||||
private RandomTools() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.SecureRandom;
|
||||
@@ -30,6 +31,8 @@ import java.util.Random;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
@SuppressWarnings("null")
|
||||
public class RandomToolsTests {
|
||||
|
||||
@@ -56,13 +59,16 @@ public class RandomToolsTests {
|
||||
|
||||
@Test
|
||||
public void randomStr_NullSourceCharacters_ThrowsException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (char[]) null, 5));
|
||||
assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, (String) null, 5));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> RandomTools.randomStr(random, (char[]) null, 5));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> RandomTools.randomStr(random, (String) null, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomStr_NegativeLength_ThrowsException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> RandomTools.randomStr(random, sourceCharactersArray, -1));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> RandomTools.randomStr(random, sourceCharactersArray, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,6 +113,50 @@ public class RandomToolsTests {
|
||||
assertEquals(5, result.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomInt_WithMinAndMax() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int r = RandomTools.randomInt(random, -2, 3);
|
||||
assertTrue(r >= -2 && r < 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomInt_WithClosedOpenRange() {
|
||||
Range<Integer> co = Range.closedOpen(-2, 3);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int rco = RandomTools.randomInt(random, co);
|
||||
assertTrue(rco >= -2 && rco < 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomInt_WithClosedRange() {
|
||||
Range<Integer> cc = Range.closed(-2, 3);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int rcc = RandomTools.randomInt(random, cc);
|
||||
assertTrue(rcc >= -2 && rcc <= 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomInt_WithOpenClosedRange() {
|
||||
Range<Integer> oc = Range.openClosed(-2, 3);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int roc = RandomTools.randomInt(random, oc);
|
||||
assertTrue(roc > -2 && roc <= 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomInt_WithOpenRange() {
|
||||
Range<Integer> oo = Range.open(-2, 3);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
int roo = RandomTools.randomInt(random, oo);
|
||||
assertTrue(roo > -2 && roo < 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
|
||||
Constructor<?>[] constructors = RandomTools.class.getDeclaredConstructors();
|
||||
|
||||
Reference in New Issue
Block a user