import java.util.Random;
即util包下有Random类,其包含了很多常用方法
常用的有
nextDouble() ===> 返回[0,1)之间的随机数(double类型)
nextFloat() ===> 返回[0,1)之间的随机数(float类型)
nextInt(n) ===> 返回[0,n)之间的整数
这些方法都不是静态的!!,所以得先创建Random对象再使用
值得注意的是,经常使用的"double x = Math.random()",其实它的实现也是调用Random来实现的
源码:
RandomNumberGeneratorHolder:
故Math.random()的实现就是调用Random类的nextDouble()方法实现的
所以一个自然而然的问题是,哪种方式更快?为什么?
① 最“无脑”给出的答案是使用Random类来实现,因为少了一层调用
② 注意到,Math类是在java.lang包下的,是默认装载到JVM上的,并且
Math类中RandomNumberGeneratorHolder类和randomNumberGenerator方法都是
static final类型的(好像static final也是先装载?),使用这样的预装载类型会不会更快些?
③ 既然Math.random的实现如上,则Math类定义中必然已经导入了Random类,查看源码果然是
所以,如果从预装载会更快些这个角度看,直接使用Random还是会更快一些:大家都预装载了,我比你少一层调用
唯一一点点疑问是:是不是java默认导入的包java.lang中的类所依赖的类也默认装载了?
不是,JVM肯定没有将Random视为默认装载的类,证据就是单独用Random时还要单独导入包
一个比较靠谱的机制应该是,默认的包应该(也应当)是一个“闭环”,不然默认导入这个含义就没意义了
猜测是,默认还是导入那些(java.lang等),如果没有使用“外包”,则不导入。。。
(有空还是研究一下JVM编译装载的顺序。。。还有默认的包的类装载是不是有什么特殊的地方)
④ 先看结果,实验一波,两种方法产生10000个随机数(nextDouble()),看哪个快
Math.random:0.154s
Random random ===> random.nextDouble:0.132s
为防止误差,多运行了几次,发觉波动很大。。。
换做100万次,都是7s多一点。。。
回到测试代码:
问题①:start要放在Random random前面
===>结果一样。。
所以虚拟机装载的时间应该和run时候没关系,import导入的包在方法运行之前就装载了
随之而来的问题是,这个时间测试代码根本没法测试。。
还有一点,就算是执行到Math.random或者Random random才装载,多次运行时,JVM应该都是使用第一次装载过的class(很显然的推测:JVM会将最近使用的class保留)
所以代码的测试时间其实只是执行100万次运行已经装载好的nextDouble() + 输出语句的时间,故没啥区别。
PS:一个有趣的现象:
System.out那一句其实是猜测,注释掉了以后发现竟然真的被优化了,多次运行时间基本稳定在0.024s(不注释是7s+),为了验证,将for注释掉只运行一次,结果?
并不是0.024s,哈哈哈
有时就是0秒(currentTimeMillis本身只能精确到ms),猜测应该是for语句本身的开销(语句本身,变量i等等等)
为了验证,将代码改为:
本想以一个break来确保执行一次,确得到还是得到单独运行一次的时间,难道说编译器真的这么厉害?能判断出加break后其实只执行一次从而不管for了??
觉得不太可能,觉得0.024s应该不仅仅是for循环的开销,用break的代码应该也是有for开销的,验证:
只循环一次,结果还是0s,基本确定了,0.024s应该不是for的开销,可能是实际运行中编译器优化所花费的时间。