1. 程式人生 > 其它 >基於XC7Z035+ADS5474的2路400Msps AD 光電脈衝採集處理卡

基於XC7Z035+ADS5474的2路400Msps AD 光電脈衝採集處理卡

HashMap 是基於雜湊表的 Map 介面的非同步實現。此實現提供所有可選的對映操作,並允許使用 null 值和 null 鍵。此類不保證對映的順序,特別是它不保證該順序恆久不變。並且,HashMap不是執行緒安全的,如果需要實現同步,則需要使用concurrentHashMap,但後者因為實現了執行緒安全,在效率上相比hashmap就比較差。
JDK1.8 之前 HashMap 底層是 陣列和連結串列 結合在一起使用也就是 連結串列雜湊。HashMap 通過 key 的 hashCode 經過擾動函式處理過後得到 hash 值,然後通過 (n - 1) & hash 判斷當前元素存放的位置(這裡的 n 指的是陣列的長度),如果當前位置存在元素的話,就判斷該元素與要存入的元素的 hash 值以及 key 是否相同,如果相同的話,直接覆蓋,不相同就通過拉鍊法解決衝突。
所謂擾動函式指的就是 HashMap 的 hash 方法。使用 hash 方法也就是擾動函式是為了防止一些實現比較差的 hashCode() 方法 換句話說使用擾動函式之後可以減少碰撞。
JDK 1.8 HashMap 的 hash 方法原始碼:

JDK 1.8 的 hash 方法 相比於 JDK 1.7 hash 方法更加簡化,但是原理不變。


    static final int hash(Object key) {
      int h;
      // key.hashCode():返回雜湊值也就是hashcode
      // ^ :按位異或
      // >>>:無符號右移,忽略符號位,空位都以0補齊
      return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

對比一下 JDK1.7 的 HashMap 的 hash 方法原始碼.

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

相比於 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的效能會稍差一點點,因為畢竟擾動了 4 次。

所謂 “拉鍊法” 就是:將連結串列和陣列相結合。也就是說建立一個連結串列陣列,陣列中每一格就是一個連結串列。若遇到雜湊衝突,則將衝突的值加到連結串列中即可。

JDK1.8 之後
相比於之前的版本, JDK1.8 之後在解決雜湊衝突時有了較大的變化,當連結串列長度大於閾值(預設為 8)(將連結串列轉換成紅黑樹前會判斷,如果當前陣列的長度小於 64,那麼會選擇先進行陣列擴容,而不是轉換為紅黑樹)時,將連結串列轉化為紅黑樹,以減少搜尋時間。

jdk1.8之後的內部結構-HashMap

TreeMap、TreeSet 以及 JDK1.8 之後的 HashMap 底層都用到了紅黑樹。紅黑樹就是為了解決二叉查詢樹的缺陷,因為二叉查詢樹在某些情況下會退化成一個線性結構。

HashMap 的長度為什麼是 2 的冪次方

為了能讓 HashMap 存取高效,儘量較少碰撞,也就是要儘量把資料分配均勻。我們上面也講到了過了,Hash 值的範圍值-2147483648 到 2147483647,前後加起來大概 40 億的對映空間,只要雜湊函式對映得比較均勻鬆散,一般應用是很難出現碰撞的。但問題是一個 40 億長度的陣列,記憶體是放不下的。所以這個雜湊值是不能直接拿來用的。用之前還要先做對陣列的長度取模運算,得到的餘數才能用來要存放的位置也就是對應的陣列下標。這個陣列下標的計算方法是“ (n - 1) & hash”。(n 代表陣列長度)。這也就解釋了 HashMap 的長度為什麼是 2 的冪次方。

這個演算法應該如何設計呢?

我們首先可能會想到採用%取餘的操作來實現。但是,重點來了:“取餘(%)操作中如果除數是 2 的冪次則等價於與其除數減一的與(&)操作(也就是說 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 並且 採用二進位制位操作 &,相對於%能夠提高運算效率,這就解釋了 HashMap 的長度為什麼是 2 的冪次方。