这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战」
项目中经常需要生成随机数来作为唯一编号进行区分,Java 中生成随机编码的方式有很多种,简单介绍一下 JDK 中自带的一些随机数生成方法。
1. 常见的随机数生成方法
1.1 Math.random()
Math 是 java.lang 包中的数学函数类,包含了常见的数值运算方法,其中 random() 方法则用于生成 [0,1) 之间的随机数,返回类型为 double 。
// Math.random()
public static String randomNumberByMath(int len){
// 根据长度计算上限值
int digit = (int)Math.pow(10,len);
int num = (int)(Math.random()*digit);
return String.valueOf(num);
}
Math.random() 生成随机数的方法,本质上是调用了 Random 对象中的 nextDouble() 方法。
- random() 方法生成的数值太小时,即使乘以上限数得到结果并转为 int 类型,也会是一个比较小的整数,需要使用字符串格式方法拼接 0 。
1.2 Random的nextInt()方法
Random 是 java.util 包中的工具类,其 nextInt() 方法在不传入参数时随机产生 int 范围内的数值;如果需要指定生成范围,则可以使用 nextInt(int bound) 传入一个大于 0 的参数,随机生成 [0,bound) 范围内的整数值。
// new Random().nextInt()
public static String randomNumberByMath(int len){
int digit = (int)Math.pow(10,len);
Random random = new Random();
int num = random.nextInt(digit);
return String.valueOf(num) ;
}
- nextInt() 方法使用时同样会生成位数不固定的整数,在位数不满足时需要补 0 。
1.3 字符串格式化
Math.random() 和 Random 的 nextInt() 方法均会产生少于设定长度的整数,因此需要在整数前拼接 0 ,并最终以字符串格式返回。
private static String formatNumberLength(int num, int len){
return String.format("%0" + len + "d",num);
}
1.4 随机索引匹配数据源方法
除了以上两种方法外,还可以定义一个数组或者字符串数据源来保存想要生成的数值,并通过随机数生成方法生成索引值,最终将生成的多个索引对应的数值拼接,得到最终需要的字符串。
public static String randomNumberFromSource(int len){
String source = "0123456789";
StringBuilder sb = new StringBuilder();
Random random = new Random();
while(len-- > 0){
sb.append(source.charAt(random.nextInt(10)));
}
return sb.toString();
}
- 由于该方法是逐位生成并拼接为字符串,因此最终结果一定是满足长度要求的。
- 因为生成的字符串是根据自定义的数据源,因此也可以生成英文字符的随机序列。
2. 进阶的随机数生成方法
2.1 Random生成随机数的安全性
2.1.1 伪随机
Random 类对象在创建时可以使用无参构造方法,或传入一个seed种子进行初始化,无参时使用当前时钟的纳秒数值作为seed参数。
而 Random 在种子相同的情况下,获取的随机数顺序是一致的,因此该生成方法属于伪随机。
2.1.2 线程安全
Random 中使用了 CAS 机制来解决线程安全问题,因此生成随机数的过程是线程安全的。
2.2 ThreadLocalRandom
ThreadLocalRandom 是 java.util.concurrent 包中的类,继承了 Random 类。ThreadLocalRandom 实现原理与 ThreadLocal 类似,其为每个线程提供了一个本地种子,来避免多个线程竞争一个种子时带来额外的性能开销。
public static void randomNumberByThreadLocalRandom(int len) throws InterruptedException {
int digit = (int)Math.pow(10,len);
for(int j = 0; j < 10; j++){
new Thread(() -> {
ThreadLocalRandom random = ThreadLocalRandom.current();
int i = 0, count = 10000;
System.out.println(random.nextInt(digit));
}
}).start();
}
}
- ThreadLocalRandom 在多线程中比 Random 更高效,不需要使用 CAS 来尝试替换以保证线程安全
- ThreadLocalRandom 不支持设置随机种子,而是在每个线程中初始化自己的种子值
2.3 SecureRandom
SecureRandom 是 java.security 包中的类,继承了 Random 类,属于安全范畴随机数生成器。 SecureRandom 对象初始化时可以选择 SHA1PRNG 或 NativePRNG 算法,默认使用 NativePRNG 算法。
public static String randomNumberBySecureRandom(int len) throws NoSuchAlgorithmException {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
int digit = (int)Math.pow(10,len);
int num = random.nextInt(digit);
return String.valueOf(num);
}
- SecureRandom 会收集一些随机事件来作为种子,随机性更好。