讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議30:不要隨便設置隨機種子 >

建議30:不要隨便設置隨機種子

隨機數在太多的地方使用了,比如加密、混淆數據等,我們使用隨機數是期望獲得一個唯一的、不可仿造的數字,以避免產生相同的業務數據造成混亂。在Java項目中通常是通過Math.random方法和Random類來獲得隨機數的,我們來看一段代碼:


public class Client{

public static void main(Stringargs){

Random r=new Random();

for(int i=1;i<4;i++){

System.out.println("第"+i+"次:"+r.nextInt());

}

}

}


代碼很簡單,我們一般都是這樣獲得隨機數的,運行此程序可知:三次打印的隨機數都不相同,即使多次運行結果也不同,這也正是我們想要隨機數的原因。我們再來看下面的程序:


public class Client{

public static void main(Stringargs){

Random r=new Random(1000);

for(int i=1;i<4;i++){

System.out.println("第"+i+"次:"+r.nextInt());

}

}

}


上面使用了Random的有參構造,運行結果如下:


第1次:-498702880

第2次:-858606152

第3次:1942818232


計算機不同輸出的隨機數也不同,但是有一點是相同的:在同一台機器上,甭管運行多少次,所打印的隨機數都是相同的,也就是說第一次運行,會打印出這三個隨機數,第二次運行還是打印出這三個隨機數,只要是在同一台硬件機器上,就永遠都會打印出相同的隨機數,似乎隨機數不隨機了,問題何在?

那是因為產生隨機數的種子被固定了,在Java中,隨機數的產生取決於種子,隨機數和種子之間的關係遵從以下兩個規則:

種子不同,產生不同的隨機數。

種子相同,即使實例不同也產生相同的隨機數。

看完上面兩個規則,我們再來看這個例子,會發現問題就出在有參構造上,Random類的默認種子(無參構造)是System.nanoTime()的返回值(JDK 1.5版本以前默認種子是System.currentTimeMillis()的返回值),注意這個值是距離某一個固定時間點的納秒數,不同的操作系統和硬件有不同的固定時間點,也就是說不同的操作系統其納秒值是不同的,而同一個操作系統納秒值也會不同,隨機數自然也就不同了。(順便說下,System.nanoTime不能用於計算日期,那是因為「固定」的時間點是不確定的,納秒值甚至可能是負值,這點與System.currentTimeMillis不同。)

new Random(1000)顯式地設置了隨機種子為1000,運行多次,雖然實例不同,但都會獲得相同的三個隨機數。所以,除非必要,否則不要設置隨機種子。

順便提一下,在Java中有兩種方法可以獲得不同的隨機數:通過java.util.Random類獲得隨機數的原理和Math.random方法相同,Math.random()方法也是通過生成一個Random類的實例,然後委託nextDouble()方法的,兩者是殊途同歸,沒有差別。

注意 若非必要,不要設置隨機數種子。