Java随机数

本文详细解析了Java中随机数生成的基本方法、高级应用及注意事项,包括使用Math.random()、java.util.Random和java.util.concurrent.ThreadLocalRandom等类生成随机数,并讨论了内存管理、并发安全和常见错误案例。

      众所周知,随机数是任何一种编程语言最基本的特征之一。而生成随机数的基本方式也是相同的:产生一个0到1之间的随机数。看似简单,但有时我们也会忽略了一些有趣的功能。

我们从书本上学到什么?

最明显的,也是直观的方式,在JAVA中生成随机数只要简单的调用:

java.lang.Math.random() 

在所有其他语言中,生成随机数就像是使用Math工具类,如abs, pow, floor, sqrt和其他数学函数。大多数人通过书籍、教程和课程来了解这个类。一个简单的例子:从0.0到1.0之间可以生成一个双精度浮点数。那么通过上面的信息,开发人员要产生0.0和10.0之间的双精度浮点数会这样来写:

Math.random() * 10 

而产生0和10之间的整数,则会写成:

Math.round(Math.random() * 10) 

进阶

通过阅读Math.random()的源码,或者干脆利用IDE的自动完成功能,开发人员可以很容易发现,java.lang.Math.random()使用一个内部的随机生成对象 - 一个很强大的对象可以灵活的随机产生:布尔值、所有数字类型,甚至是高斯分布。例如:

new java.util.Random().nextInt(10) 

它有一个缺点,就是它是一个对象。它的方法必须是通过一个实例来调用,这意味着必须先调用它的构造函数。如果在内存充足的情况下,像上面的表达式是可以接受的;但内存不足时,就会带来问题。

一个简单的解决方案,可以避免每次需要生成一个随机数时创建一个新实例,那就是使用一个静态类。猜你可能想到了java.lang.Math,很好,我们就是改良java.lang.Math的初始化。虽然这个工程量低,但你也要做一些简单的单元测试来确保其不会出错。

假设程序需要生成一个随机数来存储,问题就又来了。比如有时需要操作或保护seed,一个内部数用来存储状态和计算下一个随机数。在这些特殊情况下,共用随机生成对象是不合适的。

并发

在Java EE多线程应用程序的环境中,随机生成实例对象仍然可以被存储在类或其他实现类,作为一个静态属性。幸运的是,java.util.Random是线程安全的,所以不存在多个线程调用会破坏seed的风险。

另一个值得考虑的是多线程java.lang.ThreadLocal的实例。偷懒的做法是通过Java本身API实现单一实例,当然你也可以确保每一个线程都有自己的一个实例对象。

虽然Java没有提供一个很好的方法来管理java.util.Random的单一实例。但是,期待已久的Java 7提供了一种新的方式来产生随机数:

java.util.concurrent.ThreadLocalRandom.current().nextInt(10) 

这个新的API综合了其他两种方法的优点:单一实例/静态访问,就像Math.random()一样灵活。ThreadLocalRandom也比其他任何处理高并发的方法要更快。

经验

Chris Marasti-Georg 指出:

Math.round(Math.random() * 10) 

使分布不平衡,例如:0.0 - 0.499999将四舍五入为0,而0.5至1.499999将四舍五入为1。那么如何使用旧式语法来实现正确的均衡分布,如下:

Math.floor(Math.random() * 11) 

幸运的是,如果我们使用java.util.Random或java.util.concurrent.ThreadLocalRandom就不用担心上述问题了。

Java实战项目里面介绍了一些不正确使用java.util.Random API的危害。这个教训告诉我们不要使用:

Math.abs(rnd.nextInt())%n 

而使用:

rnd.nextInt(n)

Java中生成随机数有多种方法,以下是详细介绍: ### 使用`java.util.Random`类 这是最常用的一种生成随机数的方式,需要借助`java.util.Random`类来产生一个随机数发生器。其构造函数有两个:`Random()`和`Random(long seed)`。`Random()`是以当前时间为默认种子,`Random(long seed)`是以指定的种子值进行。种子是产生随机数的第一次使用值,机制是通过一个函数,将这个种子的值转化为随机数空间中的某一个点上,并且产生的随机数均匀的散布在空间中,以后产生的随机数都与前一个随机数有关。产生随机数发生器之后,借助不同的语句产生不同类型的数。 #### 生成0到99之间的随机整数示例代码 ```java import java.util.Random; public class RandomIntegerExample { public static void main(String[] args) { Random random = new Random(); int n = random.nextInt(100); // 生成0到99之间的随机整数 System.out.println("随机整数: " + n); } } ``` #### 获取指定范围(如5 - 10之间)的随机数示例代码 ```java import java.util.Random; public class random { public static void main(String[] args) { Random rand = new Random(); System.out.println(rand.nextInt(6) + 5); } } ``` ### 使用`Math.random()`方法 `Math.random()`方法返回的数值是`[0.0, 1.0)`的`double`型数值,由于`double`类数的精度很高,可以在一定程度下看做随机数,借助`(int)`来进行类型转换就可以得到整数随机数。示例代码如下: ```java int r = (int) (Math.random() * 100); // 生成0到99之间的随机整数 System.out.println(r); ``` ### 使用`System.currentTimeMillis()` `System.currentTimeMillis()`返回当前时间的毫秒数,可利用它来生成随机数。示例代码如下: ```java long randomNum = System.currentTimeMillis(); int k = (int) (randomNum % 100); System.out.println(k); ``` ### 关于继承`java.util.Random`类的生成器 有一种生成器继承了`java.util.Random`类,但只有一种调用方法——通过`nextInt`生成随机数,且该方法只提供无参和单个参数两种版本,其他任何调用都将直接调用`java.util.Random`方法 [^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值