1. 程式人生 > >Java 8中HashMap和LinkedHashMap如何解決衝突

Java 8中HashMap和LinkedHashMap如何解決衝突

什麼時候會產生衝突??
HashMap中呼叫hashCode()方法來計算hashCode。
由於在Java中兩個不同的物件可能有一樣的hashCode,所以不同的鍵可能有一樣hashCode,從而導致衝突的產生。

解決:

  1. 在Java 8 之前,HashMap和其他基於map的類都是通過鏈地址法解決衝突,它們使用單向連結串列來儲存相同索引值的元素。
  2. 從Java 8開始,HashMap,ConcurrentHashMap和LinkedHashMap在處理頻繁衝突時將使用平衡樹來代替連結串列,當同一hash桶中的元素數量超過特定的值(預設是8)便會由連結串列切換到平衡樹,這會將get()方法的效能從O(n)提高到O(logn)
  3. 當從連結串列切換到平衡樹時,HashMap迭代的順序將會改變。不過這並不會造成什麼問題,因為HashMap並沒有對迭代的順序提供任何保證。
  4. 從Java 1中就存在的Hashtable類為了保證迭代順序不變,即便在頻繁衝突的情況下也不會使用平衡樹。這一決定是為了不破壞某些較老的需要依賴於Hashtable迭代順序的Java應用。
  5. 除了Hashtable之外,WeakHashMap和IdentityHashMap也不會在頻繁衝突的情況下使用平衡樹。
  6. 使用HashMap之所以會產生衝突是因為使用了鍵物件的hashCode()方法,而equals()和hashCode()方法不保證不同物件的hashCode是不同的。需要記住的是,相同物件的hashCode一定是相同的,但相同的hashCode不一定是相同的物件。
  7. 在HashTable和HashMap中,衝突的產生是由於不同物件的hashCode()方法返回了一樣的值。在HashTable和HashMap中,衝突的產生是由於不同物件的hashCode()方法返回了一樣的值。

為什麼HashMap執行緒不安全:

首先如果多個執行緒同時使用put方法新增元素,而且假設正好存在兩個 put 的 key 發生了碰撞(根據 hash 值計算的 bucket 一樣),那麼根據 HashMap 的實現,這兩個 key 會新增到陣列的同一個位置,這樣最終就會發生其中一個執行緒的 put 的資料被覆蓋。

第二就是如果多個執行緒同時檢測到元素個數超過陣列大小* loadFactor ,這樣就會發生**多個執行緒同時對 Node 陣列進行擴容,**都在重新計算元素位置以及複製資料,但是最終只有一個執行緒擴容後的陣列會賦給 table,也就是說其他執行緒的都會丟失,並且各自執行緒 put 的資料也丟失。

Entry程式碼

  static class Entry<K,V> implements Map.Entry<K,V> {  
        final K key;  
        V value;  
        Entry<K,V> next;  
        final int hash;  
  
        /**  
         * Creates new entry.  
         */  
        Entry(int h, K k, V v, Entry<K,V> n) {  
            value = v;  
            next = n;  
            key = k;  
            hash = h;  
        }  
、  
    }  

導致HashMap出現死迴圈是因為多執行緒會導致HashMap的Entry節點形成環鏈,這樣當遍歷集合時Entry的next節點用於不為空,從而形成死迴圈