那個業務大拿死在了這個地方

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務代碼中的技術是每個程序員的基礎,但只是掌握了這些技巧,並不能成爲技術大牛,還要不斷打怪升級。Do more,Do better,Do exercise ,送給身邊所有程序員 !!!"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/20/204f97b53061c8158f00f580d3ccbea5.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/caf57f343267d94fb277df57e806ade8.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/96/96746cf7bced09e1b53b3b28a947acb5.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個工業級哈希表的要求:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持快速的查詢、插入、刪除操作"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內存佔用合理,不能浪費過多的內存空間"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能穩定,極端情況下,散列表的性能也不會退化到無法接受的情況"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d9f9870d55a0b416d58fff810d138c62.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f5/f5becbfa4d9885920884e79f7777b284.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 8 中哈希表底層採用數組存儲,利用 hash 算法計算出下標值來存儲元素,再配合上動態擴容,才能成爲大拿寫業務代碼的利器。在哈希表中,最最重要的是哈希函數,其次是如何解決哈希衝突。我們分別來看:"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"哈希算法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Java 8 的源碼中,hash函數的實現極其簡單:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static final int hash(Object key) {\n int h;\n return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"往數組中存儲時,利用哈希值與數組長度做按位與運算,得到數組下標:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if ((p = tab[i = (n - 1) & hash]) == null)\n\ttab[i] = newNode(hash, key, value, null);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參數key的 hashcode 是個整型值,內存中佔了32個字節,右移16位的結果是前16位都變成了0。再與hashcode值做異或操作,新的hash值的前16位也都變成了0。新的hash值,在與數組長度做按位與運算,得到數組下標。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子,計算 \"helloworld\" 作爲 key 存儲時,數據下標的計算過程:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" int h = \"hello\".hashCode();\n System.out.println(\"原始的hashcode值 :\" + getReplace(h));\n int t = h >>> 16;\n System.out.println(\"左移位16之後的值 :\" + getReplace(t));\n int r = h ^ t;\n System.out.println(\"異或結果 :\" + getReplace(r));\n int n = 15;\n System.out.println(\"數長度-1的哈希值 :\" + getReplace(n));\n int i = r & n;\n System.out.println(\"最終結果 :\" + getReplace(i));\n\n System.out.println(\"最終結果10進制 = \" + i);\n System.out.println(\"00000101111010010001100011010010\");\n}\n\nprivate static String getReplace(int r) {\n return String.format(\"%32s\", Integer.toBinaryString(r)).replace(' ', '0');\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"把計算過程的二進制運算,繪製在下圖中:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/33f1897492499d0aa21f6d0490f4ce84.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終結果 1011 轉換爲 10 進製爲11,也就是以 “hello” 爲 key 的元素,保存在數據下標 11 的位置。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"數組大小"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 hash(Object key) 函數中把 hash 值右移16位,剛是 32位字節的一半。再與自身異或,相當於用原始 hash 值的前半部分和後半部分混合,增加了 hash 的隨機性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與數組長度減一做按位與運算,相當於只保留了哈希值的低位值(後半部分)用來做數組下標。因此,要保證數組長度加一的 hash 值,高位爲 0 低位都爲 1。所以 HashMap 數組長度必須是 2 的整次冪,才能保證這一點。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/86c1e33686d722e00ffc6665850373f7.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構造函數中的確有指定參數的方法,具體跟蹤代碼在真正執行賦值時,會執行如下函數:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static final int tableSizeFor(int cap) {\n int n = cap - 1;\n n |= n >>> 1;\n n |= n >>> 2;\n n |= n >>> 4;\n n |= n >>> 8;\n n |= n >>> 16;\n return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先右移去掉低位數,再做按位或操作,相當於把結果固定在這樣的範圍:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2^{0},2^{1},2^{2},2^{3},2^{4},2^{5}……2^{n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此即使是你傳入了初始數組大小,也會調整最接近的長度範圍,所以一定是2的整次冪"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"哈希衝突"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再好的哈希算法也解決不了哈希衝突的問題,只能儘量的減少發生概率。那麼如何處理真實發生的哈希衝突呢?"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1f/1f67997205d69205d9239e326d51f8bf.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java 8 中除了用單鏈表解決哈希衝突外,還引入了紅黑樹。我們看一下源碼 (java.util.HashMap#putVal):"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"for (int binCount = 0; ; ++binCount) {\n if ((e = p.next) == null) {\n p.next = newNode(hash, key, value, null);\n if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st\n treeifyBin(tab, hash);\n break;\n }\n if (e.hash == hash &&\n ((k = e.key) == key || (key != null && key.equals(k))))\n break;\n p = e;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當鏈表上的長度大於 TREEIFY_THRESHOLD - 1 時,調用 treeifyBin() 方法。TREEIFY_THRESHOLD 爲 8,意味着,當鏈表上的數據大於等於7個時,鏈表升級爲紅黑樹。具體紅黑樹的實現,請自己賞悅代碼。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1b/1bbe8b628f452e52a526f00b278cac8d.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當數據大小需要從新計算時,在java.util.HashMap#resize 中調用 java.util.HashMap.TreeNode#split"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if (loHead != null) {\n if (lc <= UNTREEIFY_THRESHOLD)\n tab[index] = loHead.untreeify(map);\n else {\n tab[index] = loHead;\n if (hiHead != null) // (else is already treeified)\n loHead.treeify(tab);\n }\n}\nif (hiHead != null) {\n if (hc <= UNTREEIFY_THRESHOLD)\n tab[index + bit] = hiHead.untreeify(map);\n else {\n tab[index + bit] = hiHead;\n if (loHead != null)\n hiHead.treeify(tab);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果小於等於 UNTREEIFY_THRESHOLD (默認是6)執行 java.util.HashMap.TreeNode#untreeify,紅黑樹退化爲鏈表。至於紅黑樹相關的代碼,你還是自己查閱代碼吧。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/08/0878b61055346855a80c677d1554cea7.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"寫業務代碼的程序員"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個技術人員都有個成爲技術大牛的夢。工作後都會發現,夢想是成爲大牛,但做的事情看起來跟大牛都不沾邊。也總能聽到有人說,“天天寫業務代碼還加班,如何才能成爲技術大牛”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務代碼都寫不好的程序員肯定無法成爲技術大牛,只把業務代碼寫好的程序員也還不能成爲技術大牛。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"寫業務代碼,一樣可以有各種技巧,可以使得業務代碼更具可擴展性,可以和產品經理多交流以便更好的理解和實現業務,可以做好日誌記錄提升故障定位效率……"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大拿是一個業務寫的快的程序員,可能不是業務寫的好的程序員。大拿也是一個想成爲大牛的程序員,可能大拿只是想想什麼也沒做"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務代碼中的技術是每個程序員的基礎,但只是掌握了這些技巧,並不能成爲技術大牛,還要不斷打怪升級。送給所有奮鬥在業務泥潭中的程序員三個錦囊:"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do more"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"熟悉更多的業務"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解系統的全貌"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"自學用到的框架"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do better"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改進不合理、可改進的地方"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒發現有可以改進的地方,那說明功力不夠,那就繼續去發現"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Do exercise"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功利學習"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"刻意練習"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"教會別人"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"關注我"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您在微信閱讀,請您點擊鏈接 "},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MzI3MDYwOTYwOA==&scene=110#wechat_redirect","title":null},"content":[{"type":"text","text":"關注我"}]},{"type":"text","text":" ,如果您在 PC 上閱讀請掃碼關注我,歡迎與我交流隨時指出錯誤"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28d49266bc303e8d1d8779b0336982db.png","alt":null,"title":"小眼睛聊技術","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章