起因:
這個問題是在面試某公司的時候面試官提的問題,當時沒回答上來。歸根到底還是因為自己復習基礎的時候還不夠仔細,也缺乏思考。
首先
我覺得需要確認一下,是不是隨便什么情況下只要滿足了鏈表長度為8就轉紅黑樹呢?答案自然不是,為什么不是,看代碼:
/** * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. */ final void treeifyBin(Node<K,V>[] tab, int hash) { int n, index; Node<K,V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); ...... }
這是HashMap轉紅黑樹的方法代碼,可以看到,如果此時的HashMap的長度是小於MIN_TREEIFY_CAPACITY的或者為空,則進行擴容操作,而不是轉紅黑樹,這其實也是容易忽略的點。
為什么要轉紅黑樹?
回答自然很簡單,因為鏈表是取一個數需要遍歷鏈表,復雜度為O(N),而紅黑樹為O(logN)唄,那么問題來了
為什么不直接使用紅黑樹,而是要先使用鏈表實在不行再轉紅黑樹呢?
答案自然要在源碼和注釋里找:在HashMap類中第174行左右有描述:
Because TreeNodes are about twice the size of regular nodes, we
use them only when bins contain enough nodes to warrant use
(see TREEIFY_THRESHOLD)
“因為樹節點的大小是鏈表節點大小的兩倍,所以只有在容器中包含足夠的節點保證使用才用它”,顯然盡管轉為樹使得查找的速度更快,但是在節點數比較小的時候,此時對於紅黑樹來說內存上的劣勢會
超過查找等操作的優勢,自然使用鏈表更加好,但是在節點數比較多的時候,綜合考慮,紅黑樹比鏈表要好。
為什么是8,而不是9不是10?
其實當時想回答面試官這是基於統計的結果,但是心里很虛還是沒有說,再回頭看看源碼的描述:
Ideally, under random hashCodes, the frequency of nodes in bins follows a Poisson distribution
with a parameter of about 0.5 on average for the default resizing threshold of 0.75, although
with a large variance because of resizing granularity. Ignoring variance, the expected
occurrences of list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)).
The first values are: 0: 0.60653066 1: 0.30326533 2: 0.07581633 3: 0.01263606 4: 0.00157952 5: 0.00015795 6: 0.00001316 7: 0.00000094 8: 0.00000006 more: less than 1 in ten million
理想情況下,在隨機哈希碼下,哈希表中節點的頻率遵循泊松分布,而根據統計,忽略方差,列表長度為K的期望出現的次數是以上的結果,可以看到其實在為8的時候概率就已經很小了,再往后調整並
沒有很大意義。