Java中生成随机数编码方法

536 阅读3分钟

这是我参与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 会收集一些随机事件来作为种子,随机性更好。