如何在 Java

⚡ 智能摘要

随机数 Java 源自四个相关的标准库选项:Random 类、Math.random、ThreadLocalRandom 和 SecureRandom,每个选项都涵盖了实际应用中速度、线程安全性和加密强度的不同组合。

  • 🎲 随机类别: java.util.Random 公开了 nextInt、nextLong 和 next 参数。Double对于常见的原始类型,可以使用 nextBoolean。
  • 🧮 Math.random: Math.random 使用共享的 Random 实例返回介于 0.0(含)和 1.0(不含)之间的双精度浮点数。
  • 🎯 自定义范围: 给 nextInt 传递一个界限,或者缩放 Math.random 以生成在任意最小值和最大值范围内的值。
  • 🧵 螺纹安全: java.util.concurrent 中的 ThreadLocalRandom 为每个线程提供自己的生成器,在竞争情况下性能优于共享的 Random。
  • 🔐 安全性: java.security 中的 SecureRandom 会生成密码、令牌和会话密钥的加密强度值。
  • 🌱 重现性: 使用固定的长整型种子生成随机数,可以使测试和模拟在多次运行中具有确定性。

如何在 Java

随机数字无处不在 Java从单元测试数据和掷骰子游戏到会话标记和洗牌后的牌组,本教程将解释如何在各种场景下生成随机数。 Java 使用标准库中的每个选项以及如何为特定任务选择合适的选项。

在中生成随机数 Java

此 Java 标准库提供了四种生成随机数的常用方法:

  • java.util.Random 是通用类。它通过诸如以下方法生成布尔值、整数、长整型、浮点型和双精度浮点型值: nextInt, nextLongnextDouble.
  • Math.random 是一个静态辅助函数,它使用共享的双精度浮点数返回一个介于 0.0(含)和 1.0(不含)之间的双精度浮点数。 Random 内部。
  • ThreadLocalRandom in java.util.concurrent 为每个线程分配自己的生成器,因此多个线程不会争用同一个实例。
  • SecureRandom in java.security 生成密码、会话标识符和密钥所需的加密强度高的伪随机数。

以下各节展示了如何使用每种方法生成十个随机数。

示例:使用以下方式生成随机数 Java 随机类

第一个例子使用 java.util.Random 生成十个介于 0 到 99 之间的整数(包含 0 和 99):

import java.util.Random;

public class RandomNumbers {
    public static void main(String[] args) {
        Random objGenerator = new Random();
        for (int iCount = 0; iCount < 10; iCount++) {
            int randomNumber = objGenerator.nextInt(100);
            System.out.println("Random No : " + randomNumber);
        }
    }
}

输出:

Random No : 17
Random No : 57
Random No : 73
Random No : 48
Random No : 68
Random No : 86
Random No : 34
Random No : 97
Random No : 73
Random No : 18

一个实例 Random 创建方式 objGenerator。 该 nextInt(int bound) 该方法返回一个介于 0(含)和所提供的边界值(不含)之间的值,因此 nextInt(100) 返回值范围为 0 到 99。为了使序列可复现,请向构造函数传递一个种子,例如 10 ... new Random(42)同一个种子总是产生相同的序列,这在测试和模拟中很有用。

示例:使用 Java 数学随机数

要生成 0.0 到 1.0 范围内的 10 个随机值,请调用静态函数 Math.random 方法内部 循环这是在不创建生成器对象的情况下获取随机双精度浮点数的最快方法:

public class DemoRandom {
    public static void main(String[] args) {
        for (int xCount = 0; xCount < 10; xCount++) {
            System.out.println(Math.random());
        }
    }
}

输出:

0.46518450373334297
0.14859851177803485
0.5628391820492477
0.6323378498048606
0.1740198445692248
0.9140544122258946
0.9167350036262347
0.49251219841030147
0.7426056725722353
0.2039418871298877

在引擎盖下 Math.random 创建一个静态的 Random 首次使用和通话 nextDouble 确实如此。这个单一的共享生成器对于演示来说没问题,但在繁重的多线程负载下会成为瓶颈,接下来的章节将对此进行讨论。

生成自定义范围内的随机数

实际程序通常需要一个特定范围内的随机值,例如掷骰子需要一个 1 到 6 之间的整数,或者模拟需要一个 10.0 到 20.0 之间的双精度浮点数。两种模式几乎可以涵盖所有情况。

介于最小值和最大值之间的随机整数(包含最小值和最大值):

import java.util.Random;

public class RangeDemo {
    public static void main(String[] args) {
        Random rnd = new Random();
        int min = 1;
        int max = 6;
        // nextInt(max - min + 1) returns 0..(max-min), then shift by min
        int dice = rnd.nextInt(max - min + 1) + min;
        System.out.println("Dice roll : " + dice);
    }
}

随机生成一个介于最小值和最大值之间的双精度浮点数:

double min = 10.0;
double max = 20.0;
double value = min + (max - min) * Math.random();
System.out.println("Value : " + value);

这两种模式的工作原理都是从生成器的原生范围内取一个值,将其缩放到目标范围的宽度,然后向上移动最小值。如果您正在使用…… Java 在 8 或更高版本中,可以通过较新的方法获得相同的结果。 ints, longsdoubles 流方法 Random, 例如 rnd.ints(10, 1, 7).forEach(System.out::println) 掷十次骰子。

多线程的 ThreadLocalRandom Code

单一共享 Random 虽然线程安全,但在多线程竞争的情况下速度较慢,因为每个线程都必须在同一个内部种子上进行同步。 ThreadLocalRandom添加于 Java 7岁,住在 java.util.concurrent这样,每个线程都有自己的生成器,就不会出现锁争用。

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalDemo {
    public static void main(String[] args) {
        // random int in [1, 100]
        int i = ThreadLocalRandom.current().nextInt(1, 101);

        // random long in [1000, 5000]
        long l = ThreadLocalRandom.current().nextLong(1000L, 5001L);

        // random double in [0.0, 1.0)
        double d = ThreadLocalRandom.current().nextDouble();

        System.out.println(i + " " + l + " " + d);
    }
}

绝大部分储备使用 ThreadLocalRandom.current() 在工作线程、并行流和任何并发任务内部。请注意。 setSeedUnsupportedOperationException因此,不支持使用种子数据来实现结果复现;如果您需要固定的种子数据,请使用普通模式。 Random.

SecureRandom 用于安全敏感型随机数生成器 Numbers

实例 RandomThreadLocalRandom 并非加密安全的。攻击者只需少量输出样本即可重构内部状态并预测未来的值。对于密码、会话令牌、盐值和加密密钥,请使用 java.security.SecureRandom 相反,它从操作系统熵池中提取种子素材,并产生难以预测的值。

import java.security.SecureRandom;

public class SecureDemo {
    public static void main(String[] args) {
        SecureRandom secure = new SecureRandom();

        byte[] token = new byte[16];
        secure.nextBytes(token);          // 128-bit random token

        int otp = secure.nextInt(1_000_000); // 6-digit one-time code

        System.out.println("OTP : " + String.format("%06d", otp));
    }
}

SecureRandomRandom 因为它使用了更强大的算法和更丰富的种子数据。建议将其用于安全相关工作,而将速度更快的生成器用于游戏、模拟和测试数据。

常见问题

创建一个 java.util.Random 实例并调用 nextInt、nextLong 和 next 方法。Double或者使用 `nextBoolean`。如果想快速生成随机数,可以使用 `Math.random`,它会返回一个介于 0.0 和 1.0 之间的双精度浮点数。这两种方法都包含在标准库中。

对于整数,调用 `random.nextInt(max – min + 1) + min`;对于双精度浮点数,调用 `min + (max – min) * Math.random()`。这些公式会将生成器的范围缩放到目标窗口,并向下移动最小值。

Random 是一个功能齐全的类,包含多种基本数据类型的方法,并支持随机数种子。Math.random 是一个静态辅助方法,返回一个介于 0.0 和 1.0 之间的双精度浮点数。在内部,Math.random 调用的是一个共享的 Random 实例。

是的,但是并发调用会争用同一个内部随机数生成器,这会降低多线程代码的运行速度。使用 java.util.concurrent 包中的 ThreadLocalRandom.current() 方法,为每个线程分配一个独立的随机数生成器,即可消除这种争用。

将一个固定的长整型值传递给 Random 构造函数,例如 new Random(42),或者调用 setSeed(42)。生成器每次运行都会生成相同的序列,这对于可重复的测试和模拟至关重要。

当需要确保生成的值不易被预测时,例如密码、会话令牌、盐值、一次性代码和加密密钥,请使用 SecureRandom。Plain Random 虽然速度快但结果可预测,因此不适用于安全应用。

机器学习框架使用伪随机数生成器来初始化权重、打乱训练数据并划分验证集。固定随机种子可以确保实验结果的可复现性,从而使团队能够可靠地比较模型运行结果并调试问题。

是的。GitHub Copilot 会生成 Random、ThreadLocalRandom 和 SecureRandom 代码片段,并匹配相应的范围缩放公式。 JUnit 试验。 Rev查看每个建议,以确认所选类符合使用场景,特别是对于安全敏感代码。

总结一下这篇文章: