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

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

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

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理
    1、散列表要解決的一個(gè)問題就是散列值的沖突問題,通常是兩種方法:鏈表法和開放地址法。鏈表法就是將相同hash值的對(duì)象組織成一個(gè)鏈表放在hash值對(duì)應(yīng)的槽位;開放地址法是通過一個(gè)探測算法,當(dāng)某個(gè)槽位已經(jīng)被占據(jù)的情況下繼續(xù)查找下一個(gè)可以使用的槽位。java.util.HashMap采用的鏈表法的方式,鏈表是單向鏈表,因此在刪除過程中要自己維持prev節(jié)點(diǎn),我想不采用雙向鏈表是從節(jié)省空間考慮。一個(gè)典型的查找過程:
    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采用鏈表法而不是開放地址法,猜想可能的原因是從實(shí)用角度出發(fā),對(duì)空間和時(shí)間效率做出的折中選擇。采用開放地址法,無論是線性探測或者二次探測都可能造成群集現(xiàn)象,而雙重散列會(huì)要求散列表的裝填程度比較低的情況下會(huì)有比較好的查找效率,容易造成空間的浪費(fèi)。

    2、什么是負(fù)載因子?負(fù)載因子a定義為
         a=散列表的實(shí)際元素?cái)?shù)目(n)/ 散列表的容量(m)

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

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

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

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

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

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

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

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

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

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

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




    主站蜘蛛池模板: 免费黄色app网站| 精品国产日韩亚洲一区| 中文字幕亚洲综合久久菠萝蜜| 91精品国产亚洲爽啪在线观看| 男人的天堂av亚洲一区2区| 成全高清在线观看免费 | 99久久婷婷免费国产综合精品| 国产在线观看麻豆91精品免费| 亚洲精品人成无码中文毛片| 亚洲人成人77777在线播放| aaa毛片视频免费观看| 成年女人男人免费视频播放| 亚洲国产精品无码成人片久久| 亚洲自偷自偷在线成人网站传媒| 国产一级高青免费| 欧亚精品一区三区免费| 久久久青草青青亚洲国产免观| 亚洲另类无码专区丝袜| 久操免费在线观看| 亚洲v国产v天堂a无码久久| 亚洲AV成人无码天堂| 成人免费区一区二区三区 | 伊人婷婷综合缴情亚洲五月| 亚洲中文无码卡通动漫野外| 久久大香香蕉国产免费网站| 亚洲国产精品日韩| 亚洲AV日韩综合一区尤物| 无码国产精品一区二区免费式芒果 | 久久久久久久亚洲精品| 亚洲国产午夜精品理论片在线播放| 91视频免费网址| 色噜噜AV亚洲色一区二区| 亚洲αⅴ无码乱码在线观看性色| 四虎成年永久免费网站| 亚洲国产精品乱码一区二区| 视频一区二区三区免费观看| 全免费a级毛片免费**视频| 久久精品国产亚洲AV香蕉| 9久热精品免费观看视频| 免费va人成视频网站全| 日韩亚洲不卡在线视频中文字幕在线观看 |