第五十九條:了解和使用類庫

假設(shè)你希望產(chǎn)生位于0和某個(gè)上界之間的隨機(jī)整數(shù)。面對這個(gè)常見的任務(wù)击喂,許多程序員會編寫如下所示的方法:

// Common but deeply flawed!
static Random rnd = new Random();
static int random(int n) {
  return Math.abs(rnd.nextInt()) % n;
}

這個(gè)方法看起來可能不錯(cuò)维苔,但是卻又三個(gè)缺點(diǎn),第一個(gè)缺點(diǎn)是懂昂,如果n是一個(gè)比較小的2的乘方介时,經(jīng)過一段相當(dāng)短的周期之后,它會產(chǎn)生的隨機(jī)數(shù)序列將會重復(fù)凌彬。第二個(gè)缺點(diǎn)是沸柔,如果n不是2的乘方,那么平均起來铲敛,有些數(shù)會比其他的數(shù)出現(xiàn)得更為頻繁褐澎。如果n比較大,這個(gè)缺點(diǎn)就會非常明顯伐蒋。這可以通過下面的程序直觀的體現(xiàn)出來工三,它會產(chǎn)生100萬個(gè)經(jīng)過精心指定的范圍內(nèi)的隨機(jī)數(shù)迁酸,并打印出有多少個(gè)數(shù)字落在隨機(jī)數(shù)取值范圍的前半部分:

public static void main(String[] args) { 
  int n = 2 * (Integer.MAX_VALUE / 3); 
  int low = 0;
  for (int i = 0; i < 1000000; i++)
    if (random(n) < n/2) 
      low++;
  System.out.println(low); 
}

如果random方法工作正常,這個(gè)程序打印出來的數(shù)將接近于100萬的一半俭正,但是如果真正運(yùn)行這個(gè)程序奸鬓,就會發(fā)現(xiàn)它打印出的數(shù)接近于666666。由于random方法產(chǎn)生的數(shù)字有三分之二落在隨機(jī)數(shù)取值范圍的前半部分段审。

random方法的第三個(gè)缺點(diǎn)是全蝶,在極少數(shù)情況下,它的失敗是災(zāi)難性的寺枉,因?yàn)闀祷匾粋€(gè)落在指定范圍之外的數(shù)抑淫。之所以如此,是因?yàn)檫@個(gè)方法試圖通過調(diào)用Math.abs姥闪,將rnd.nextInt()返回的值隱射為一個(gè)非負(fù)整數(shù)int始苇。如果nextInt()返回Integer.MIN_VALUE,那么Math.abs也會返回Integer.MAX_VALUE筐喳,假設(shè)n不是2的乘方催式,那么取模操作符(%)將返回一個(gè)負(fù)數(shù)。這幾乎肯定會使程序失敗避归,而且這種失敗很難重現(xiàn)荣月。

為了編寫能修正這三個(gè)缺點(diǎn)的random方法,有必要了解關(guān)于同余隨機(jī)數(shù)生成器梳毙、數(shù)論和2的求補(bǔ)算法的相關(guān)知識哺窄。幸運(yùn)的是,你并不需要自己來做這些工作账锹,已經(jīng)有現(xiàn)成的成果可以為你所用萌业。這一成果被稱作Random.nextInt(int)。你無須關(guān)系nextInt(int)的實(shí)現(xiàn)細(xì)節(jié)(如果你有強(qiáng)烈的好奇心奸柬,可以研究它的文檔或者源代碼)生年。具有算法背景的高級工程師已經(jīng)花了大量的時(shí)間來設(shè)計(jì)、實(shí)現(xiàn)和測試這個(gè)方法廓奕,然后經(jīng)過這個(gè)領(lǐng)域中的專家的審查抱婉,以確保它的正確性。之后桌粉,標(biāo)準(zhǔn)類庫經(jīng)過了Beta測試并正式發(fā)行授段,幾年之間已經(jīng)有成千上萬的程序員在使用它。在這個(gè)方法中還沒有發(fā)現(xiàn)過缺陷番甩,但是侵贵,如果將來發(fā)現(xiàn)有缺陷,在下一個(gè)發(fā)行版本就會修正這些缺陷缘薛。通過使用標(biāo)準(zhǔn)類庫窍育,可以充分利用這些編寫標(biāo)準(zhǔn)的專家的知識卡睦,以及在你之前的其他人的使用經(jīng)驗(yàn)

從Java7開始漱抓,就不應(yīng)該再使用Random了表锻。現(xiàn)在選擇隨機(jī)數(shù)生成器時(shí),大多使用ThreadLocalRandom乞娄。它會產(chǎn)生更高質(zhì)量的隨機(jī)數(shù)瞬逊,并且速度非常快仪或。在我的機(jī)器上确镊,比Random快了3.6倍。對于Fork Join Pool和并行Stream范删,則使用SplittableRandom蕾域。

使用標(biāo)準(zhǔn)庫的第二個(gè)好處是,不必浪費(fèi)時(shí)間為那些與工作不太相關(guān)的問題提供特別的解決方案到旦。就像大多數(shù)程序員一樣旨巷,應(yīng)該把時(shí)間放在應(yīng)用程序上,而不是底層細(xì)節(jié)上添忘。

使用標(biāo)準(zhǔn)庫的第三個(gè)好處是采呐,它們的性能往往會隨著時(shí)間的推移而不斷提高,無須你做任何努力搁骑。因?yàn)樵S多人在使用它們懈万,并且是當(dāng)工業(yè)標(biāo)準(zhǔn)在使用,所以提供這些標(biāo)準(zhǔn)類庫的組織有強(qiáng)烈的動機(jī)要使它們運(yùn)行得更快靶病。這些年來,許多Java平臺類庫已經(jīng)被重新編寫了口予,有時(shí)候是重復(fù)編寫娄周,而是在性能上有了顯著的提高。

使用標(biāo)準(zhǔn)庫的第四個(gè)好處是沪停,它們會隨著時(shí)間的推移而增加新的功能煤辨。如果類庫中漏掉了某些功能,開發(fā)者社區(qū)就會把這些缺點(diǎn)公式出來木张,漏掉的功能就會添加到后續(xù)的發(fā)行版本中众辨。

使用標(biāo)準(zhǔn)類庫的最后一個(gè)好處是,可以使自己的代碼融入主流舷礼。這樣的代碼更易讀鹃彻、更易維護(hù)、更易被大多數(shù)的開發(fā)人員重用妻献。

既然有那么多的有點(diǎn)蛛株,使用標(biāo)準(zhǔn)類庫機(jī)制而不選擇專門的實(shí)現(xiàn)团赁,這顯然是符合邏輯的,然而還是有相當(dāng)一部分的程序員沒有這么做谨履。為什么呢欢摄?可能它們并不知道有這些類庫機(jī)制的存在。在每個(gè)重要的發(fā)行版本中笋粟,都會有許多新的特性被加入到類庫中怀挠,所以與這些新特性保持同步是值得的。每當(dāng)Java平臺有重要的發(fā)行時(shí)害捕,都會發(fā)布一個(gè)網(wǎng)頁來說明新的特性绿淋。舉個(gè)例子,假設(shè)想要編寫一個(gè)程序吨艇,用它打印出命令行中指定的一條URL的內(nèi)容(Linux的curl命令的作用大體如此)躬它。在Java9之前,這些代碼有點(diǎn)煩瑣东涡,但是Java9在InputStream中增加了transferTo方法冯吓。下面就是利用這個(gè)新方法完成這項(xiàng)任務(wù)的完整程序:

// Printing the contents of a URL with transferTo, added in Java 9
public static void main(String[] args) throws IOException {
  try (InputStream in = new URL(args[0]).openStream()) { 
    in.transferTo(System.out);
  } 
}

這些標(biāo)準(zhǔn)類庫太龐大了,以至于不可能學(xué)完所有的文檔疮跑,但是每個(gè)程序員都應(yīng)該熟悉java.lang组贺、java.util、java.io及其子包中的內(nèi)容祖娘。
關(guān)于其他類庫的知識可以根據(jù)需要隨時(shí)學(xué)習(xí)失尖。總結(jié)類庫中的機(jī)制超出了本條目的范圍渐苏,幾年來它們已經(jīng)發(fā)展得十分龐大了掀潮。

其中有幾個(gè)類庫值得一提。Collections Framework(集合框架)和Stream類庫(詳見第45條至48條)應(yīng)該成為每一為程序員基本工具箱中的一部分琼富,同樣也應(yīng)該成為java.util.concurrent中并發(fā)機(jī)制的組成部分仪吧。這個(gè)包既包含高級的并發(fā)工具來簡化多線程的編程任務(wù),還包含低級別的并發(fā)基本類型鞠眉,運(yùn)行專家們自己編寫更高級的并發(fā)抽象薯鼠。關(guān)于java.util.concurrent的高級部分,請參閱第80條和第81條械蹋。

在某些情況下出皇,一個(gè)類庫工具并不能滿足你的需求。你的需求越是特殊哗戈,這種情形就越有可能發(fā)生郊艘。雖然你的第一個(gè)念頭應(yīng)該是使用標(biāo)準(zhǔn)類庫,但是,如果你在觀察了它們的某些領(lǐng)域所提供的功能之后暇仲,確定不能滿足需要步做,你就得使用其他的實(shí)現(xiàn)。任何一組類庫所提供的功能總是難免會有遺漏奈附。如果你在Java類庫中找不到所需要的功能全度,下一個(gè)選擇應(yīng)該是在高級的第三方類庫中去尋找,比如Goole優(yōu)秀的開源Guava類庫斥滤。如果在所有相應(yīng)的類庫中都無法找到你所需的功能将鸵,就只能自己實(shí)現(xiàn)這些功能了。

總而言之佑颇,不要重復(fù)發(fā)明輪子顶掉。如果你要做的事情看起來是十分常見的,有可能類庫中已經(jīng)有某個(gè)類完成了這樣的工作挑胸。如果確實(shí)是這樣痒筒,就使用現(xiàn)成的;如果還不清楚是否存在這樣的類茬贵,就去查一查簿透。一般而言,類庫的代碼可能比你自己編寫的代碼更好一些解藻,并且會隨著時(shí)間的推移而不斷改進(jìn)老充。這并不是在質(zhì)疑你作為一個(gè)程序員的能力。從經(jīng)濟(jì)角度的分析表明:類庫代碼受到的關(guān)注遠(yuǎn)遠(yuǎn)超過大多數(shù)普通程序員在同樣功能上所能給予的投入螟左。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啡浊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胶背,更是在濱河造成了極大的恐慌巷嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钳吟,死亡現(xiàn)場離奇詭異廷粒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)砸抛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來树枫,“玉大人直焙,你說我怎么就攤上這事∩扒幔” “怎么了奔誓?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我厨喂,道長和措,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任蜕煌,我火速辦了婚禮派阱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘斜纪。我一直安慰自己贫母,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布盒刚。 她就那樣靜靜地躺著腺劣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪因块。 梳的紋絲不亂的頭發(fā)上橘原,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音涡上,去河邊找鬼趾断。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吓懈,可吹牛的內(nèi)容都是我干的歼冰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼耻警,長吁一口氣:“原來是場噩夢啊……” “哼隔嫡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起甘穿,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤腮恩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后温兼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秸滴,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年募判,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荡含。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡届垫,死狀恐怖释液,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情装处,我是刑警寧澤误债,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響寝蹈,放射性物質(zhì)發(fā)生泄漏李命。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一箫老、第九天 我趴在偏房一處隱蔽的房頂上張望封字。 院中可真熱鬧,春花似錦槽惫、人聲如沸周叮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仿耽。三九已至,卻和暖如春各薇,著一層夾襖步出監(jiān)牢的瞬間项贺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工峭判, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留开缎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓林螃,卻偏偏與公主長得像奕删,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子疗认,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容