讀《阿里巴巴Java開發(fā)手冊》的一點(diǎn)思考

1.前言

最近在上下班的地鐵上把《阿里巴巴Java開發(fā)手冊》讀了一遍,感覺獲益匪淺。讀的過程中也有一些思考和疑惑,就抽時(shí)間親自嘗試了一下。下面用這篇文章把我的問題和答案整理出來。

2.正文

  1. 手冊里提到,如果重寫equals()方法,就必須重寫hashCode()方法,網(wǎng)上查了一下如何重寫hashCode(),發(fā)現(xiàn)好多重寫方法里都出現(xiàn)了31這個(gè)神奇的數(shù)字。以下是String類的hashCode()實(shí)現(xiàn):
@Override public int hashCode() {
        int hash = hashCode;
        if (hash == 0) {
            if (count == 0) {
                return 0;
            }
            for (int i = 0; i < count; ++i) {
                hash = 31 * hash + charAt(i);
            }
            hashCode = hash;
        }
        return hash;
    }

這個(gè)magic number勾起了我的興趣,重寫hashCode()方法為什么會(huì)不約而同地用到31這個(gè)數(shù)字呢?
在網(wǎng)上查了一下,這個(gè)問題也沒有標(biāo)準(zhǔn)的答案,以下是我查找到的一些比較令人信服的答案:

  • 每個(gè)對象根據(jù)值計(jì)算HashCode,這個(gè)code大小雖然不奢求必須唯一(因?yàn)檫@樣通常計(jì)算會(huì)非常慢),但是要盡可能的不要重復(fù),因此基數(shù)要盡量的大且計(jì)算出來的值不要溢出,計(jì)算出來的hash地址越大,所謂的“沖突”就越少,查找起來效率也會(huì)提高。
  • 素?cái)?shù)的特性能夠使得它和其他數(shù)相乘后得到的結(jié)果比其他方式更容易產(chǎn)成唯一性,也就是hashcode值的沖突概率最小。
  • 31*N可以被編譯器優(yōu)化為左移5位后減N,有較高的性能。

比較權(quán)威的答案是Stack OverFlow上:
According to Joshua Bloch's『Effective Java』:
The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting.
The advantage of using a prime is less clear, but it is traditional.
A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i.
Modern VMs do this sort of optimization automatically.

翻譯:
根據(jù) Joshua Bloch 的著作『Effective Java』:
設(shè)計(jì)者選擇 31 這個(gè)值是因?yàn)樗且粋€(gè)奇質(zhì)數(shù)。如果它是一個(gè)偶數(shù),在使用乘法當(dāng)中產(chǎn)生數(shù)值溢出時(shí),原有數(shù)字的信息將會(huì)丟失,因?yàn)槌艘远喈?dāng)于位移。
選擇質(zhì)數(shù)的優(yōu)勢不是那么清晰,但是這是一個(gè)傳統(tǒng)。
31 的一個(gè)優(yōu)良的性質(zhì)是:乘法可以被位移和減法替代: 31 * i == (i << 5) - i
現(xiàn)代的 VM 可以自行完成這個(gè)優(yōu)化。

  1. 手冊里還留下了一個(gè)問題:
List<String> a = new ArrayList<String>();
        a.add("1");
        a.add("2");
        for (String temp : a) {
            if ("1".equals(temp)) {
                a.remove(temp);
            }
        }

這段代碼執(zhí)行時(shí)不會(huì)拋出異常,但是如果將equals前面的"1"換成"2"就會(huì)拋出ConcurrentModificationException,使用Iterator來進(jìn)行刪除操作也不會(huì)拋出異常,這是為什么呢?

通過分析源碼發(fā)現(xiàn),在Iterator的remove()和next()方法中都調(diào)用了checkForComodification()方法:

final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }

就是在這里拋出了ConcurrentModificationException.

modCount:修改次數(shù)
expectedModCount:預(yù)期的修改次數(shù)

使用ArrayList的remove(),只會(huì)使modCount++,不會(huì)修改expectedModCount
使用Iterator的remove(),則是expectedModCount = ++modCount

前面提到remove("1")時(shí)不會(huì)拋出異常,這是因?yàn)閒oreach方法其實(shí)也是調(diào)用了Iterator的hasNext()和next(),next()里調(diào)了checkForComodification(),hasNext()卻沒調(diào)用,remove("1")后,hasNext()返回false,就不會(huì)走到next(),所以也就不會(huì)拋出異常了,這其實(shí)只是一個(gè)巧合。
所以:不要在foreach循環(huán)里進(jìn)行元素的remove/add操作,remove元素請使用Iterator方式,如果并發(fā)操作,需要對Iterator對象加鎖。
具體的分析過程可以參考:http://blog.csdn.net/qq_28816195/article/details/78433544

3.結(jié)語

參加工作后才發(fā)現(xiàn),代碼規(guī)范其實(shí)是極其重要的,優(yōu)雅的代碼便于理解和維護(hù),更加節(jié)省時(shí)間。就像通過一個(gè)人的字就能大概看出這是一個(gè)什么樣的人,代碼亦是如此。剛畢業(yè)不久的我,更應(yīng)該培養(yǎng)好的代碼規(guī)范,這對今后的成長必然是大有益處的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,978評論 2 374

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