<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    java.util.HashMap源碼要點淺析

    Posted on 2009-04-15 12:33 dennis 閱讀(4385) 評論(0)  編輯  收藏 所屬分類: java數據結構與算法源碼解讀
    1、散列表要解決的一個問題就是散列值的沖突問題,通常是兩種方法:鏈表法和開放地址法。鏈表法就是將相同hash值的對象組織成一個鏈表放在hash值對應的槽位;開放地址法是通過一個探測算法,當某個槽位已經被占據的情況下繼續查找下一個可以使用的槽位。java.util.HashMap采用的鏈表法的方式,鏈表是單向鏈表,因此在刪除過程中要自己維持prev節點,我想不采用雙向鏈表是從節省空間考慮。一個典型的查找過程:
    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 != null && key.equals(k))))
                    
    return e;
     }
       HashMap采用鏈表法而不是開放地址法,猜想可能的原因是從實用角度出發,對空間和時間效率做出的折中選擇。采用開放地址法,無論是線性探測或者二次探測都可能造成群集現象,而雙重散列會要求散列表的裝填程度比較低的情況下會有比較好的查找效率,容易造成空間的浪費。

    2、什么是負載因子?負載因子a定義為
         a=散列表的實際元素數目(n)/ 散列表的容量(m)

    負載因子衡量的是一個散列表的空間的使用程度,負載因子越大表示散列表的裝填程度越高,反之愈小。對于使用鏈表法的散列表來說,查找一個元素的平均時間是 O(1+a),因此如果負載因子越大,對空間的利用更充分,然而后果是查找效率的降低;如果負載因子太小,那么散列表的數據將過于稀疏,對空間造成嚴重浪費。

    回到HashMap的實現,HashMap中的loadFactor其實定義的就是該map對象允許的最大的負載因子,如果超過這個系數將重新resize。這個是通過threshold字段來判斷,看threshold的計算:
    threshold = (int)(capacity * loadFactor);

    結合上面的負載因子的定義公式可知,threshold就是在此loadFactor和capacity對應下允許的最大元素數目,超過這個數目就重新resize,以降低實際的負載因子。默認的的負載因子0.75是對空間和時間效率的一個平衡選擇。注意到的一點是resize的規模是現有 capacity的兩倍:
      if (size++ >= threshold)
                resize(
    2 * table.length);
     
    3、可能你也注意到了,java.util.HashMap對key的hash值多做了一步處理,而不是直接使用hashCode:

    static int hash(int h) {
            h 
    ^= (h >>> 20^ (h >>> 12);
            
    return h ^ (h >>> 7^ (h >>> 4);
      }

    這個處理的原因在于HashMap的容量總是采用2的p次冪,而取index(槽位)的方法是
    static int indexFor(int h, int length) {
            
    return h & (length-1);
     }

    這一運算等價于對length取模,也就是
           h % 2^p
    返回的將是h的p個最低位組成的數字,我們假設hash輸入是符合簡單一致散列,然而這一假設并不能推論出hash的p個最低位也會符合簡單一致散列,也許h的這p個最低位相同的幾率很大,那么沖突的幾率就非常大了。優秀的散列函數應該需要考慮所有的位。

    因此為了防止這些“壞”的散列函數造成效率的降低,HashMap預先對hash值做了處理以考慮到所有的位,根據注釋也可以知道。這個處理我看不懂,留待高人解釋,也許來自于某本算法書也不一定。

    4、我們知道java.util.HashMap不是線程安全的,因此如果在使用迭代器的過程中有其他線程修改了map,那么將拋出ConcurrentModificationException,這就是所謂fail-fast策略(速錯),這一策略在源碼中的實現是通過 modCount域,modCount顧名思義就是修改次數,對HashMap內容的修改都將增加這個值,那么在迭代器初始化過程中會將這個值賦給迭代器的expectedModCount,

     HashIterator() {
                expectedModCount 
    = modCount;
                
    if (size > 0) { // advance to first entry
                    Entry[] t = table;
                    
    while (index < t.length && (next = t[index++]) == null)
                        ;
                }
            }

    在迭代過程中,判斷modCount跟expectedModCount是否相等,如果不相等就表示已經有其他線程修改了map

      
    final Entry<K,V> nextEntry() {
                
    if (modCount != expectedModCount)
                    
    throw new ConcurrentModificationException();
     注意到modCount聲明為volatile,保證線程之間修改的可見性。
     




    主站蜘蛛池模板: 国产成人亚洲精品狼色在线| 亚洲色精品88色婷婷七月丁香| 国产亚洲视频在线观看| 亚洲综合另类小说色区| 和日本免费不卡在线v| 国产亚洲福利一区二区免费看| 亚洲av福利无码无一区二区 | 1000部啪啪毛片免费看| 亚洲欧美精品午睡沙发| 国产亚洲老熟女视频| 免费不卡视频一卡二卡| caoporm超免费公开视频| 亚洲伊人久久大香线蕉啊| 亚洲人成色77777在线观看大| 91频在线观看免费大全| 国产国产人免费人成成免视频| 亚洲av乱码一区二区三区| 亚洲乱色熟女一区二区三区丝袜| 成人浮力影院免费看| 成全视成人免费观看在线看| 亚洲国产激情在线一区| 亚洲无线码在线一区观看| 在线观着免费观看国产黄| 99久久99热精品免费观看国产| 特a级免费高清黄色片| 亚洲人成伊人成综合网久久| 亚洲国产无套无码av电影| 国产免费av一区二区三区| 综合在线免费视频| 精品成人免费自拍视频| 黄色毛片免费网站| 亚洲久热无码av中文字幕 | 亚洲AV无码国产精品永久一区| 亚洲第一中文字幕| 久久久久亚洲精品天堂久久久久久 | 亚洲天堂2017无码中文| 亚洲永久永久永久永久永久精品| 国产乱辈通伦影片在线播放亚洲 | 久久久久久久亚洲Av无码| 精品亚洲综合在线第一区| 亚洲成a人片在线观看久|