從網上已經身邊同事朋友的面試狀況來看,面試
HashMap
幾乎是必問的,網上也不少相似的文章,可是真面起來,發現仍是有不少點能夠深摳的。本篇就結合一次面試經歷說一下以前沒有注意的點吧。java
這個相信不用我多說,你們都知道HashMap
的底層是Node
數組結構Node<K,V>[] table
。面試
擴容也不用我多說了,在size
達到閾值(默認0.75
的負載因子*容量)時觸發擴容。數組
數組的capacity
大小是2的x冪也無需多言,但這裏多問一句爲何是2的x冪而不是其餘數呢?咱們知道,當一個key
被放進到數組時須要明確本身被放在哪一個位置。最簡單的固然就是對key
進行hash
以後h%n
肯定。而若是數組的長度n
是2的x冪,h%n
這個操做與h&(n-1)
是等價的,會更快。同時在擴容時,每一個key
須要從新肯定本身在數組中的index
,這時若是數組每一個位置的元素都變了一次,顯然開銷會比較大。可是若是n
是2的x冪,那麼在擴容變成2n
後須要從新確認index
時,對某個table[index]
這個元素的新位置只有兩種可能:1. 在原地不動(若是h&n
的高位爲0),2. index+n
(h&n
的高位爲1)。這樣每一個元素移動的機率只有50%,顯然會節約不少拷貝操做。框架
這個也是高頻問點,你們也基本都清楚,JDK1.8以後,若是某位置的鏈表長度大於某個閾值以後,就會轉爲紅黑樹,防止鏈表深度過大,從而查詢時複雜度達到o(n)
最壞狀況。可是細問起來,若是沒有認真看過putVal
方法中每一行代碼,真的有的地方可能會忽略。好比:ide
MIN_TREEIFY_CAPACITY
默認64這個值,會觸發一次擴容而並不會執行轉樹操做,因此鏈表的長度是能夠超過8的。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(); else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K,V> hd = null, tl = null; do { TreeNode<K,V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) hd.treeify(tab); } }
key
應該放在左子樹仍是右子樹?這個由什麼肯定?由於HashMap
的key
並不要求是Comparable
,而TreeMap
很顯然key
是要知足Comparable
的,那麼此時新來一個TreeNode
,左右肯定以什麼爲依據呢?TreeNode
應該掛在它父節點的左邊仍是右邊,掛在哪邊均可以啊,只要我插入時按某個標準,查找時也按一樣的標準,二者保持一致就能夠了,對吧?跟到源代碼,對於沒有實現Comparable
的key
,比較一下hashCode就能夠了。源碼中的比較一句就是兩個key
的hashCode
,使用的是System.identityHashCode(object)
這個native方法。static int tieBreakOrder(Object a, Object b) { int d; if (a == null || b == null || (d = a.getClass().getName(). compareTo(b.getClass().getName())) == 0) d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); return d; }
還有你們都耳熟能詳的東西我就不贅述了,面後也思考了一下,基礎仍是很重要,仍是有不少指的深刻思考的地方,必定要打牢基礎,可能準備了不少框架原理實踐什麼的,若是基礎的沒答好,這些應用層的東西準備的再好,可能也沒機會跟面試官聊了,固然在面試中如何去引導面試官這一點也很重要,俗話說的好,把對方拉倒跟我一個低智商區,而後用我豐富的經驗戰勝他,這一點很重要,之後要多注意。code