posts - 167,  comments - 30,  trackbacks - 0

          基礎很重要哦...

          1.       HashMap工作原理:

          HashMap是基于Hash表的Map接口的非同步實現。此實現提供所有可選的映射操作,并允許使用null作為鍵和值。無序。

           

          2.       HashMap數據結構:

          HashMap實際上是一個“鏈表散列”的數據結構,即是數組和鏈表的結合體。HashMap底層就是個數組結構,數組的每一項是一個鏈表。當新建一個HashMap時,就會新創建一個數組。

          Java代碼:

          /**

            * 長度擴容必須是2的倍數

            */

          transient Entry[] table;

           

          static class Entry<K,V> implements Map.Entry<K,V> {

                  final K key;

                  V value;

                  Entry<K,V> next;

                  final int hash;

          … …

          public HashMap() {

                  this.loadFactor = DEFAULT_LOAD_FACTOR;

                  threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);

                  table = new Entry[DEFAULT_INITIAL_CAPACITY];

                  init();

          }

          可以看到,每個Entry就是一個鍵值對,本身對象持有下一個對象的引用,這樣就構成了鏈表。

          為了元素在HashMap中均勻分布,通常想到的是把hashCode對數組長度取模運算,但是取模運算的消耗比較大,那么HashMap做法是根據key算的的hashCode跟數組-1進行“與”運算(&)

          將初始大小設置為16的原因(當擴容是必須是2的整數次冪):主要為了使HashMap的訪問的性能最高,減少keybucket中存取時的碰撞幾率。

           3.       HashMapresize

          HashMap的元素越來越多時,碰撞的幾率就越來越高,因為數組的長度初始時是固定的,所以為了提高查詢的效率,就要對HashMap的數組進行擴容,在HashMap數組擴容后最消耗性能點是:原數組中的元素必須重新計算在新數組中的位置,然后存放進去。

           什么時候擴容?

          HashMap中的元素個數超過數組大小*負載因子的時,會進行數組擴容,默認的loadFactor0.75(意思是當一個Map填滿了75%bucket的時),也就是當為1216*0.75),就會把數組擴容原來大小的兩倍:16*2=32。然后重新計算每個元素在數組中的位置(此時比較消耗性能了)。 這個過程也叫做rehashing(因為它調用了hash方法找到新bucket的位置)。

          建議當我們已預知了數組的元素個數,可根據具體需求自行設置數組初始容量以便提高查詢性能。但是要記得考慮“&”的問題。這樣也解決了resize的問題。

           

          4. HashMap的存取實現:

          put方法分析:

          如果傳入keynull值,則將其放倒數組的第一個位置。如果key不為空,首先對key調用hashCode方法,對返回的hashCode值做hash,通過計算hash值可以找到bucket(這個bucket就是指Entry數組)位置(下標)來存儲Entry對象。

          public V put(K key, V value) {

                  if (key == null)

                      return putForNullKey(value);

                  int hash = hash(key.hashCode());

                  int i = indexFor(hash, table.length);

                  for (Entry<K,V> e = table[i]; e != null; e = e.next) {

                      Object k;

                      if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

                          V oldValue = e.value;

                          e.value = value;

                          e.recordAccess(this);

                          return oldValue;

                      }

                  }

           

                  modCount++;

                  addEntry(hash, key, value, i);

                  return null;

          }

           

           

          雖然發生碰撞的幾率較小,但是如果發生碰撞,則會將新添加的元素放倒鏈表頭部,早先加入的元素放倒鏈表尾部(我們可以將發生碰撞的這個鏈表理解為LinkedList,這個LinkedList中存儲了鍵值對形式的Map.Entry對象)。

          void addEntry(int hash, K key, V value, int bucketIndex) {

              Entry<K,V> e = table[bucketIndex];

                  table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

                  if (size++ >= threshold)

                      resize(2 * table.length);

          }

           get方法:

          調用get方法時,首先會根據傳入的key調用hashCode方法,計算hash值找到bucket位置,然后遍歷鏈表(即上面所說的linkedList<Entry<K,V>>),判斷hashkeyequals方法查找到對應的Entry對象。

          public V get(Object key) {

                  if (key == null)

                      return getForNullKey();

                  int hash = hash(key.hashCode());

                  for (Entry<K,V> e = table[indexFor(hash, table.length)];

                       e != null;

                       e = e.next) {

                      Object k;

                      if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

                          return e.value;

                  }

                  return null;

          }

           最佳實踐方式:

          使用不可變的、聲明作final的對象,并且采用合適的equals()hashCode()方法的話,將會減少碰撞的發生,提高效率。不可變性使得能夠緩存不同鍵的hashcode,這將提高整個獲取對象的速度,使用StringInterger這樣的wrapper類作為鍵是非常好的選擇。

           參考:http://www.importnew.com/7099.html

          posted on 2013-11-18 18:01 David1228 閱讀(2907) 評論(4)  編輯  收藏 所屬分類: JAVA

          FeedBack:
          # re: 源代碼分析之HashMap
          2013-11-19 10:08 | 鵬達鎖業
          支持博主分享  回復  更多評論
            
          # re: 源代碼分析之HashMap[未登錄]
          2013-11-19 11:29 | David
          常來^^, 多謝支持O(∩_∩)O哈~ @鵬達鎖業
            回復  更多評論
            
          # re: 源代碼分析之HashMap
          2013-12-15 00:01 | 左岸
          寫的挺詳細,謝謝分享  回復  更多評論
            
          # re: 源代碼分析之HashMap
          2014-01-08 15:54 | Alex_Woo
          今天斷點調試,進入到.put方法時候,Key 和value都無法查看結果 modCount++;比如這一行,可以看到modCount的值,不曉得什么原因。想看Entry的值,也看不到.....什么原因呀?因為是泛型的原因嗎??  回復  更多評論
            

          <2013年11月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          常用鏈接

          留言簿(4)

          隨筆分類

          隨筆檔案

          文章檔案

          新聞分類

          新聞檔案

          相冊

          收藏夾

          Java

          Linux知識相關

          Spring相關

          云計算/Linux/虛擬化技術/

          友情博客

          多線程并發編程

          開源技術

          持久層技術相關

          搜索

          •  

          積分與排名

          • 積分 - 359229
          • 排名 - 154

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 阜城县| 平利县| 突泉县| 平塘县| 金平| 湟中县| 漾濞| 泸定县| 麻阳| 缙云县| 修文县| 边坝县| 西宁市| 乌恰县| 闽清县| 澄迈县| 玛曲县| 亚东县| 海口市| 襄樊市| 灵川县| 商河县| 晋江市| 滨海县| 即墨市| 北京市| 丘北县| 赣州市| 元江| 桓台县| 甘谷县| 民县| 彰化市| 姚安县| 丰顺县| 清原| 嵊州市| 翼城县| 三台县| 丹东市| 田林县|