隨筆-204  評論-149  文章-0  trackbacks-0

          Hashtable的結構,采用的是數據結構中所說的鏈地址法處理沖突的方法

          從上面的結構圖可以看出,Hashtable的實質就是一個數組+鏈表。圖中的Entry就是鏈表的實現,Entry的結構中包含了對自己的另一個實例的引用next,用以指向另外一個Entry。而圖中標有數字的部分是一個Entry數組,數字就是這個Entry數組的index。那么往Hashtable增加鍵值對的時候,index會根據鍵的hashcode、Entry數組的長度共同決定,從而決定鍵值對存放在Entry數組的哪個位置。從這種意義來說,當鍵一定,Entry數組的長度一定的情況下,所得到的index肯定是相同的,也就是說插入順序應該不會影響輸出的順序才對。然而,還有一個重要的因素沒有考慮,就是計算index出現相同值的情況。譬如代碼中 "sichuan" 和 "anhui",所得到的index是相同的,在這個時候,Entry的鏈表功能就發揮作用了:put方法通過Entry的next屬性獲得對另外一個Entry的引用,然后將后來者放入其中。根據debug得出的結果,"sichuan", "anhui"的index同為2,"hunan"的index為6,"beijing"的index為1,在輸出的時候,會以index遞減的方式獲得鍵值對。很明顯,會改變的輸出順序只有"sichuan"和"anhui"了,也就是說輸出只有兩種可能:"hunan" - "sichuan" - "anhui" - "beijing"和"hunan" - "anhui" - "sichuan" - "beijing"。以下是運行了示例代碼之后,Hashtable的結果:


          在Hashtable的實現代碼中,有一個名為rehash的方法用于擴充Hashtable的容量。很明顯,當rehash方法被調用以后,每一個鍵值對相應的index也會改變,也就等于將鍵值對重新排序了。這也是往不同容量的Hashtable放入相同的鍵值對會輸出不同的鍵值對序列的原因。在Java中,觸發rehash方法的條件很簡單:hahtable中的鍵值對超過某一閥值。默認情況下,該閥值等于hashtable中Entry數組的長度×0.75。

           

          自 Java 2 平臺 v1.2 以來,此類已經改進為可以實現 Map,因此它變成了 Java Collections Framework 的一部分。與新集合的實現不同,Hashtable 是同步的。

          由迭代器返回的 Iterator 和由所有 Hashtable 的“collection 視圖方法”返回的 Collection 的 listIterator 方法都是快速失敗 的:在創建 Iterator 之后,如果從結構上對 Hashtable 進行修改,除非通過 Iterator 自身的移除或添加方法,否則在任何時間以任何方式對其進行修改,Iterator 都將拋出 ConcurrentModificationException因此,面對并發的修改,Iterator 很快就會完全失敗,而不冒在將來某個不確定的時間發生任意不確定行為的風險。由 Hashtable 的鍵和值方法返回的 Enumeration 不 是快速失敗的。

          注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步并發修改做出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。因此,為提高這類迭代器的正確性而編寫一個依賴于此異常的程序是錯誤做法:迭代器的快速失敗行為應該僅用于檢測程序錯誤。

          TestHashTable
          Hashtable中定義幾個內部類(包括靜態的嵌套類和普通的內部類)

          Hashtable中的Entry數據結構
          Entry

          put方法:key的hash值不同但是可能放入的index相同,并且在放入之前需要判斷
          put方法

           1    public synchronized Enumeration<K> keys() {
           2    return this.<K>getEnumeration(KEYS);
           3    }

           4
           5    private <T> Enumeration<T> getEnumeration(int type) {
           6    if (count == 0{
           7        return (Enumeration<T>)emptyEnumerator;
           8    }
           else {
           9        return new Enumerator<T>(type, false);
          10    }

          11    }

          12
          13    public synchronized Enumeration<V> elements() {
          14    return this.<V>getEnumeration(VALUES);
          15    }

          16
          17Enumerator是Hashtable定義的一個內部類
          18    private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
          19    Entry[] table = Hashtable.this.table;//訪問宿主類的成員變量
          20    int index = table.length;
          21    Entry<K,V> entry = null;
          22    Entry<K,V> lastReturned = null;
          23    int type;
          24
          25    /**
          26     * Indicates whether this Enumerator is serving as an Iterator
          27     * or an Enumeration.  (true -> Iterator).
          28     */

          29    boolean iterator;
          30
          31    /**
          32     * The modCount value that the iterator believes that the backing
          33     * Hashtable should have.  If this expectation is violated, the iterator
          34     * has detected concurrent modification.
          35     */

          36    protected int expectedModCount = modCount;
          37}

          38內部類中提供了訪問了hashtable中的entry數組的方法.
          39在實現Iterator接口中的方法時使用了expectedModCount變量來判斷是否有并發修改而導致fast-fail,而在Enumeration的接口方法實現中沒有判斷
          40
          41
          42
          43    public Set<K> keySet() {
          44    if (keySet == null)
          45        keySet = Collections.synchronizedSet(new KeySet(), this);
          46    return keySet;
          47    }

          48    private class KeySet extends AbstractSet<K> {
          49        public Iterator<K> iterator() {
          50        return
           getIterator(KEYS);
          51        }

          52        public int size() {
          53            return count;
          54        }

          55        public boolean contains(Object o) {
          56            return containsKey(o);
          57        }

          58        public boolean remove(Object o) {
          59            return Hashtable.this.remove(o) != null;
          60        }

          61        public void clear() {
          62            Hashtable.this.clear();
          63        }

          64    }

          65內部類KeySet中有iterator接口方法的實現,調用的宿主類的getIterator(KEYS)
          66    private <T> Iterator<T> getIterator(int type) {
          67    if (count == 0{
          68        return (Iterator<T>) emptyIterator;
          69    }
           else {
          70        return new Enumerator<T>(type, true);
          71    }

          72    }

          73getIterator中new 了一個新的內部類Enumerator的對象,最終使用Enumerator來訪問hashtable的entry數組,能不能在內部類中直接創建一個內部類的的實例???
          74
          75
          76
          77    public Collection<V> values() {
          78    if (values==null)
          79        values = Collections.synchronizedCollection(new ValueCollection(),
          80                                                        this);
          81        return values;
          82    }

          83ValueCollection也是一個內部類,結構和KeySet功能差不多
          84    public Set<Map.Entry<K,V>> entrySet() {
          85    if (entrySet==null)
          86        entrySet = Collections.synchronizedSet(new EntrySet(), this);
          87    return entrySet;
          88    }

          89EntrySet也是內部類,結構和KeySet功能差不多

          posted on 2009-07-03 13:24 Frank_Fang 閱讀(8871) 評論(1)  編輯  收藏 所屬分類: Java編程

          評論:
          # re: Java Hashtable分析 2009-07-15 00:11 | Frank_Fang
          public class HashMap<K,V> 
          extends AbstractMap<K,V>
          implements Map<K,V>, Cloneable, Serializable
          
          

          基于哈希表的 Map 接口的實現。此實現提供所有可選的映射操作,并允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恒久不變。

          此實現假定哈希函數將元素正確分布在各桶之間,可為基本操作(getput)提供穩定的性能。迭代集合視圖所需的時間與 HashMap 實例的“容量”(桶的數量)及其大小(鍵-值映射關系數)的和成比例。所以,如果迭代性能很重要,則不要將初始容量設置得太高(或將加載因子設置得太低)。

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

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

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

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

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

          由所有此類的“集合視圖方法”所返回的迭代器都是快速失敗 的:在迭代器創建之后,如果從結構上對映射進行修改,除非通過迭代器自身的 removeadd 方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間任意發生不確定行為的風險。

          注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用于檢測程序錯誤。



          public class LinkedHashMap<K,V> 
          extends HashMap<K,V>
          implements Map<K,V>

          Map 接口的哈希表和鏈接列表實現,具有可預知的迭代順序。此實現與 HashMap 的不同之處在于,后者維護著一個運行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,該迭代順序通常就是將鍵插入到映射中的順序(插入順序)。注意,如果在映射中重新插入 鍵,則插入順序不受影響。(如果在調用 m.put(k, v)m.containsKey(k) 返回了 true,則調用時會將鍵 k 重新插入到映射 m 中。)

          此實現可以讓客戶避免未指定的、由 HashMap(及 Hashtable)所提供的通常為雜亂無章的排序工作,同時無需增加與 TreeMap 相關的成本。使用它可以生成一個與原來順序相同的映射副本,而與原映射的實現無關:

               void foo(Map m) {
          Map copy = new LinkedHashMap(m);
          ...
          }
          
          如果模塊通過輸入得到一個映射,復制這個映射,然后返回由此副本確定其順序的結果,這種情況下這項技術特別有用。(客戶通常期望返回的內容與其出現的順序相同。)

          提供特殊的構造方法來創建鏈接哈希映射,該哈希映射的迭代順序就是最后訪問其條目的順序,從近期訪問最少到近期訪問最多的順序(訪問順序)。這種映射很適合構建 LRU 緩存。調用 putget 方法將會訪問相應的條目(假定調用完成后它還存在)。putAll 方法以指定映射的條目集合迭代器提供的鍵-值映射關系的順序,為指定映射的每個映射關系生成一個條目訪問。任何其他方法均不生成條目訪問。特別是,collection 視圖上的操作 影響底層映射的迭代順序。

          可以重寫 removeEldestEntry(Map.Entry) 方法來實施策略,以便在將新映射關系添加到映射時自動移除舊的映射關系。

          此類提供所有可選的 Map 操作,并且允許 null 元素。與 HashMap 一樣,它可以為基本操作(addcontainsremove)提供穩定的性能,假定哈希函數將元素正確分布到桶中。由于增加了維護鏈接列表的開支,其性能很可能比 HashMap 稍遜一籌,不過這一點例外:LinkedHashMap 的 collection 視圖迭代所需時間與映射的大小 成比例。HashMap 迭代時間很可能開支較大,因為它所需要的時間與其容量 成比例。

          鏈接的哈希映射具有兩個影響其性能的參數:初始容量加載因子。它們的定義與 HashMap 極其相似。要注意,為初始容量選擇非常高的值對此類的影響比對 HashMap 要小,因為此類的迭代時間不受容量的影響。

          注意,此實現不是同步的。如果多個線程同時訪問鏈接的哈希映射,而其中至少一個線程從結構上修改了該映射,則它必須 保持外部同步。這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這一操作,以防止意外的非同步訪問:

              Map m = Collections.synchronizedMap(new LinkedHashMap(...));
          
          結構修改是指添加或刪除一個或多個映射關系,或者在按訪問順序鏈接的哈希映射中影響迭代順序的任何操作。在按插入順序鏈接的哈希映射中,僅更改與映射中已包含鍵關聯的值不是結構修改。在按訪問順序鏈接的哈希映射中,僅利用 get 查詢映射不是結構修改。

          Collection(由此類的所有 collection 視圖方法所返回)的 iterator 方法返回的迭代器都是快速失敗 的:在迭代器創建之后,如果從結構上對映射進行修改,除非通過迭代器自身的移除方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發的修改,迭代器很快就會完全失敗,而不冒將來不確定的時間任意發生不確定行為的風險。

          注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步并發修改做出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常的程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用于檢測程序錯誤。

          此類是 Java Collections Framework 的成員。

            回復  更多評論
            
          主站蜘蛛池模板: 乌拉特中旗| 禹城市| 宕昌县| 绥阳县| 修水县| 高淳县| 中牟县| 宾川县| 昭平县| 西乌珠穆沁旗| 高州市| 辉南县| 昭苏县| 招远市| 抚远县| 贺州市| 孟村| 溧水县| 禹城市| 江永县| 成安县| 旌德县| 陆丰市| 成都市| 仲巴县| 图木舒克市| 乐昌市| 若尔盖县| 巨鹿县| 富川| 集安市| 鸡西市| 辽源市| 诸暨市| 汶川县| 奉新县| 哈尔滨市| 泸西县| 肇庆市| 深州市| 汶川县|