天天看點

java産生随機數的方法

最明顯的,也是直覺的方式,在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 指出:

使分布不平衡,例如: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)