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

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

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

    HashMap解析

    HashMap是基于哈希表的Map接口的實現,允許null值和null鍵(HashMap和Hashtable大致一樣,除了不同步和允許null外)。HashMap不保存映射的順序,特別是不保證該順序恒久不變。

           HashMap的實現假定hash函數將各個元素正確分布在各桶之間,可為基本操作(如get()和set())提供穩定的性能,迭代集合視圖所需的時間與HashMap 實例的“容量”(桶的數量)及其大小(鍵-值映射關系數)的和成比例。所以,如果迭代性能很重要,則不要將初始容量設置得太高(或將加載因子設置得太低)。 (注:這段同HashSet很相似,其實HashSet是基本HashMap實現的,所以在性能的控制上也一樣)

           HashMap 的實例有兩個參數影響其性能:初始容量和加載因子。容量是哈希表中桶的數量,初始容量只是哈希表在創建時的容量。加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,通過調用 rehash 方法將容量翻倍。HashMap的默認初始容量是16,默認加載因子是0.75,這樣,當HashMap里的元素超過16*0.75即12時,調用rehash方法,使容量增長一倍。

           通常,默認加載因子 (.75) 在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap類的操作中,包括 get 和 put 操作,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地降低 rehash 操作次數。如果初始容量大于最大條目數除以加載因子,則不會發生 rehash 操作。

           如果很多映射關系要存儲在 HashMap 實例中,則相對于按需執行自動的 rehash 操作以增大表的容量來說,使用足夠大的初始容量創建它將使得映射關系能更有效地存儲。        

           注意,此實現不是同步的。 如果多個線程同時訪問此映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。(結構上的修改是指添加或刪除一個或多個映射關系的操作;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這一操作,以防止對映射進行意外的不同步訪問,如下所示:

         Map m = Collections.synchronizedMap(new HashMap(...));      

    由所有此類的“集合視圖方法”所返回的迭代器都是快速失敗的:在迭代器創建之后,如果從結構上對映射進行修改,除非通過迭代器

    自身的 removeadd 方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException 。因此,面對并發

    的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間任意發生不確定行為的風險。

    注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大

    努力拋出 ConcurrentModificationException 。因此,編寫依賴于此異常程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應

    該僅用于檢測程序錯誤 HashMap的構造函數:

    public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);

            // Find a power of 2 >= initialCapacity
            int capacity = 1;
            while (capacity < initialCapacity)
                capacity <<= 1;
       
            this.loadFactor = loadFactor;
            threshold = (int)(capacity * loadFactor);
            table = new Entry[capacity];
            init();
        }

    可以看出,當生成HashMap對象時,如果initialCapacity的數值比較大時,capacity 會被賦于等于或大于initialCapacity 的值,然后

    創建一個有capacity 個Entry對象的table數組,所以initialCapacity的數值不能太大,否則會生成比較大的數組table,占用比較大的應用內

    存,造成內存浪費。

    HashMap的默認構造函數:

    public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR;
            threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
            table = new Entry[DEFAULT_INITIAL_CAPACITY];
            init();
        }

    默認構造函數里的初始容量為16,加載因子為0.75,這樣會生成含有16*0.75=12個Entry對象的數組table,當HashMap里的元素數

    量超過12個時,會將HashMap的容量翻1倍,生成包含32個Entry對象的數組。

    put方法:

                public V put(K key, V value) {
            K k = maskNull(key);
            int hash = hash(k);
            int i = indexFor(hash, table.length);

            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                if (e.hash == hash && eq(k, e.key)) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }

            modCount++;
            addEntry(hash, k, value, i);
            return null;
        }

            K k = maskNull(key);這行是判斷key是否為null,如果為null,則返回一個靜態的K(Object)對象做為鍵值,這就是HashMap為什么允許null鍵存在的原因。

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

            這連續的兩步就是 HashMap 最牛的地方!研究完我都汗顏了,其中 hash 就是通過 key 這個Object的 hashcode 進行 hash,然后通過 indexFor 獲得在Object table的索引值。

                 table???不要驚訝,其實HashMap也神不到哪里去,它就是用 table 來放的。最牛的就是用 hash 能正確的返回索引。其中的hash算法,我跟JDK的作者 Doug 聯系過,他建議我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他這樣一提,我就更加急了,可惜口袋空空啊!!!
    不知道大家有沒有留意 put 其實是一個有返回的方法,它會把相同鍵值的 put 覆蓋掉并返回舊的值!如下方法徹底說明了 HashMap 的結構,其實就是一個表加上在相應位置的Entry的鏈表:
    for (Entry e = table; e != null; e = e.next) {
    if (e.hash == hash && eq(k, e.key)) {
    Object oldvalue = e.value;
    e.value = value; //把新的值賦予給對應鍵值。
    e.recordAccess(this); //空方法,留待實現
    return oldvalue; //返回相同鍵值的對應的舊的值。
    }
    }
    modCount++; //結構性更改的次數
    addEntry(hash, k, value, i); //添加新元素,關鍵所在!
    return null; //沒有相同的鍵值返回

    我們把關鍵的方法拿出來分析:
    void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]); 
    因為 hash 的算法有可能令不同的鍵值有相同的hash碼并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它經過indexfor之后的索引一定都為i,這樣在new的時候這個Entry的next就會指向這個原本的 table,再有下一個也如此,形成一個鏈表,和put的循環對定e.next獲得舊的值。到這里,HashMap的結構,大家也十分明白了吧?
    if (size++ >= threshold) //這個threshold就是能實際容納的量
    resize(2 * table.length); //超出這個容量就會將Object table重構 
    所謂的重構也不神,就是建一個兩倍大的table(我在別的論壇上看到有人說是兩倍加1,把我騙了),然后再一個個indexfor進去!注意!!這就是效率!!如果你能讓你的HashMap不需要重構那么多次,效率會大大提高!
    說到這里也差不多了,get比put簡單得多,大家,了解put,get也差不了多少了。對于collections我是認為,它是適合廣泛的,當不完 全適合特有的,如果大家的程序需要特殊的用途,自己寫吧,其實很簡單。(作者是這樣跟我說的,他還建議我用LinkedHashMap,我看了源碼以后發 現,LinkHashMap其實就是繼承HashMap的,然后override相應的方法,有興趣的同人,自己looklook)建個 Object table,寫相應的算法,就ok啦。
    舉個例子吧,像 Vector,list 啊什么的其實都很簡單,最多就多了的同步的聲明,其實如果要實現像Vector那種,插入,刪除不多的,可以用一個Object table來實現,按索引存取,添加等。
    如果插入,刪除比較多的,可以建兩個Object table,然后每個元素用含有next結構的一個table存,如果要插入到i,但是i已經有元素,用next連起來,然后size++,并在另一個table記錄其位置。

    posted on 2011-01-10 09:39 himalayas 閱讀(667) 評論(0)  編輯  收藏 所屬分類: JAVA


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    <2011年1月>
    2627282930311
    2345678
    9101112131415
    16171819202122
    23242526272829
    303112345

    導航

    統計

    常用鏈接

    留言簿

    隨筆分類(15)

    隨筆檔案(16)

    最新隨筆

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲第一成年男人的天堂| 国产国产人免费人成免费视频 | 四虎在线免费播放| 亚洲精品国产成人99久久| 亚洲国产亚洲综合在线尤物| 热99RE久久精品这里都是精品免费| 免费福利电影在线观看| 久久精品国产亚洲Aⅴ蜜臀色欲| 久久亚洲国产成人精品性色| 亚洲综合综合在线| 亚洲日韩在线中文字幕综合 | 日韩亚洲人成网站| 国产精品成人无码免费| 亚洲综合精品成人| 久久久久国产精品免费免费搜索 | 亚洲精品无码mⅴ在线观看| 99久久免费观看| 亚洲电影一区二区| 99在线观看免费视频| 亚洲av丰满熟妇在线播放| 三年片在线观看免费| 免费黄色毛片视频| 亚洲经典千人经典日产| 操美女视频免费网站| 亚洲女初尝黑人巨高清| 玖玖在线免费视频| 99ri精品国产亚洲| 国产日韩在线视频免费播放| 久久精品国产亚洲AV不卡| 国产成人自产拍免费视频| 亚洲综合伊人久久大杳蕉| 丝瓜app免费下载网址进入ios| 日本不卡视频免费| 色吊丝免费观看网站| 亚洲精品网站在线观看不卡无广告| 亚洲一级黄色大片| 久久久久国色av免费看| 亚洲 另类 无码 在线| 精品国产成人亚洲午夜福利| 国产成人精品免费久久久久| 久久亚洲AV成人无码软件|