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

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

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

    隨筆-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 閱讀(8865) 評論(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 的成員。

      回復  更多評論
      
    主站蜘蛛池模板: 国产精品亚洲综合专区片高清久久久| 成年丰满熟妇午夜免费视频| 免费在线观看的黄色网址| 亚洲真人无码永久在线观看| 999国内精品永久免费视频| 亚洲国产精品日韩在线| 99久热只有精品视频免费观看17| 亚洲日韩图片专区第1页| 久久99热精品免费观看牛牛| 亚洲AV日韩AV鸥美在线观看| 久久久高清日本道免费观看| 亚洲一区二区成人| 99re视频精品全部免费| 亚洲人成在线精品| 好男人视频在线观看免费看片| 极品色天使在线婷婷天堂亚洲| 亚洲人成网站色在线入口| 国产在线观看免费av站| 日韩亚洲AV无码一区二区不卡| 日韩免费一区二区三区在线 | 免费一区二区三区四区五区| 免费精品国产自产拍在线观看| 亚洲午夜福利精品无码| 免费人成毛片动漫在线播放| 97亚洲熟妇自偷自拍另类图片| 美女被cao免费看在线看网站| 亚洲AV无码一区二区三区久久精品| 免费成人av电影| 日本免费高清视频| 国产精品亚洲午夜一区二区三区| 日本不卡视频免费| 中文字幕永久免费视频| 亚洲激情黄色小说| 成人伊人亚洲人综合网站222| 波多野结衣免费一区视频 | 67pao强力打造国产免费| 亚洲日韩精品国产3区| 国产亚洲自拍一区| 日韩不卡免费视频| 人人爽人人爽人人片av免费 | 亚洲人妻av伦理|