Java 集合系列 10 Hashtable詳細介紹(源碼解析)和使用示例


java 集合系列目錄:

Java 集合系列 01 總體框架

Java 集合系列 02 Collection架構

Java 集合系列 03 ArrayList詳細介紹(源碼解析)和使用示例

Java 集合系列 04 LinkedList詳細介紹(源碼解析)和使用示例 

Java 集合系列 05 Vector詳細介紹(源碼解析)和使用示例

Java 集合系列 06 Stack詳細介紹(源碼解析)和使用示例

Java 集合系列 07 List總結(LinkedList, ArrayList等使用場景和性能分析)

Java 集合系列 08 Map架構

Java 集合系列 09 HashMap詳細介紹(源碼解析)和使用示例

Java 集合系列 10 Hashtable詳細介紹(源碼解析)和使用示例

Java 集合系列 11 hashmap 和 hashtable 的區別

Java 集合系列 12 TreeMap

Java 集合系列 13 WeakHashMap

Java 集合系列 14 hashCode

Java 集合系列 15 Map總結

Java 集合系列 16 HashSet

Java 集合系列 17 TreeSet

 

 

前一章,我們學習了HashMap。這一章,我們對Hashtable進行學習。
我們先對Hashtable有個整體認識,然后再學習它的源碼,

第1部分 Hashtable介紹
第2部分 Hashtable數據結構
第3部分 Hashtable主要方法

  3.1 put()

    3.2 get()

  3.3 putAll()

  3.4 clear()

  3.5 contains() 和 containsValue()

  3.6 containsKey()

  3.7 elements()

  3.8 remove()

 第4部分 Hashtable實現的Cloneable接口

 第5部分 Hashtable實現的Serializable接口

第1部分 Hashtable介紹

Hashtable 簡介

 和HashMap一樣,Hashtable 也是一個散列表,它存儲的內容是鍵值對(key-value)映射

Hashtable 繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable接口。
Hashtable 的函數都是同步的,這意味着它是線程安全的。它的key、value都不可以為null。此外,Hashtable中的映射不是有序的。

此類實現一個哈希表,該哈希表將鍵映射到相應的值。任何非 null 對象都可以用作鍵或值。

為了成功地在哈希表中存儲和獲取對象,用作鍵的對象必須實現 hashCode 方法和 equals 方法。

Hashtable 的實例有兩個參數影響其性能:初始容量 和 加載因子。容量 是哈希表中桶 的數量,初始容量 就是哈希表創建時的容量。注意,哈希表的狀態為 open:在發生“哈希沖突”的情況下,單個桶會存儲多個條目,這些條目必須按順序搜索。加載因子 是對哈希表在其容量自動增加之前可以達到多滿的一個尺度。初始容量和加載因子這兩個參數只是對該實現的提示。關於何時以及是否調用 rehash 方法的具體細節則依賴於該實現。
通常,默認加載因子是 0.75, 這是在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查找某個條目的時間(在大多數 Hashtable 操作中,包括 get 和 put 操作,都反映了這一點)。

 

Hashtable的構造函數

1.默認構造函數,容量為11,加載因子為0.75。

public Hashtable() {
        this(11, 0.75f);
    }

 

2.用指定初始容量和默認的加載因子 (0.75) 構造一個新的空哈希表。

public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

 

3.用指定初始容量和指定加載因子構造一個新的空哈希表。

public Hashtable(int initialCapacity, float loadFactor) {
        //驗證初始容量
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        //驗證加載因子
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;

        this.loadFactor = loadFactor;

        //初始化table,獲得大小為initialCapacity的table數組
        table = new Entry[initialCapacity];
        //計算閥值
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        //初始化HashSeed值
        initHashSeedAsNeeded(initialCapacity);
    }

其中initHashSeedAsNeeded方法用於初始化hashSeed參數,其中hashSeed用於計算key的hash值,它與key的hashCode進行按位異或運算。這個hashSeed是一個與實例相關的隨機值,主要用於解決hash沖突。

private int hash(Object k) {
        // hashSeed will be zero if alternative hashing is disabled.
        return hashSeed ^ k.hashCode();
    }

 

4.構造一個與給定的 Map 具有相同映射關系的新哈希表。

public Hashtable(Map<? extends K, ? extends V> t) {
        //設置table容器大小,其值==t.size * 2 + 1
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

 

Hashtable的API

void clear() 
          將此哈希表清空,使其不包含任何鍵。 
 Object clone() 
          創建此哈希表的淺表副本。 
 boolean contains(Object value) 
          測試此映射表中是否存在與指定值關聯的鍵。 
 boolean containsKey(Object key) 
          測試指定對象是否為此哈希表中的鍵。 
 boolean containsValue(Object value) 
          如果此 Hashtable 將一個或多個鍵映射到此值,則返回 true。 
 Enumeration<V> elements() 
          返回此哈希表中的值的枚舉。 
 Set<Map.Entry<K,V>> entrySet() 
          返回此映射中包含的鍵的 Set 視圖。 
 boolean equals(Object o) 
          按照 Map 接口的定義,比較指定 Object 與此 Map 是否相等。 
 V get(Object key) 
          返回指定鍵所映射到的值,如果此映射不包含此鍵的映射,則返回 null. 更確切地講,如果此映射包含滿足 (key.equals(k)) 的從鍵 k 到值 v 的映射,則此方法返回 v;否則,返回 nullint hashCode() 
          按照 Map 接口的定義,返回此 Map 的哈希碼值。 
 boolean isEmpty() 
          測試此哈希表是否沒有鍵映射到值。 
 Enumeration<K> keys() 
          返回此哈希表中的鍵的枚舉。 
 Set<K> keySet() 
          返回此映射中包含的鍵的 Set 視圖。 
 V put(K key, V value) 
          將指定 key 映射到此哈希表中的指定 value。 
 void putAll(Map<? extends K,? extends V> t) 
          將指定映射的所有映射關系復制到此哈希表中,這些映射關系將替換此哈希表擁有的、針對當前指定映射中所有鍵的所有映射關系。 
protected  void rehash() 
          增加此哈希表的容量並在內部對其進行重組,以便更有效地容納和訪問其元素。 
 V remove(Object key) 
          從哈希表中移除該鍵及其相應的值。 
 int size() 
          返回此哈希表中的鍵的數量。 
 String toString() 
          返回此 Hashtable 對象的字符串表示形式,其形式為 ASCII 字符 ", " (逗號加空格)分隔開的、括在括號中的一組條目。 
 Collection<V> values() 
          返回此映射中包含的鍵的 Collection 視圖。 

 

第2部分 Hashtable數據結構

Hashtable的繼承關系

java.lang.Object
   ↳     java.util.Dictionary<K, V>
         ↳     java.util.Hashtable<K, V>

public class Hashtable<K,V> extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable { }

 

Hashtable與Map關系如下圖:

 

從圖中可以看出: 
(01) Hashtable繼承於Dictionary類,實現了Map接口。Map是"key-value鍵值對"接口,Dictionary是聲明了操作"鍵值對"函數接口的抽象類。 
(02) Hashtable是通過"拉鏈法"實現的哈希表。它包括幾個重要的成員變量:table, count, threshold, loadFactor, modCount。
  table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。 
  count是Hashtable的大小,它是Hashtable保存的鍵值對的數量。 
  threshold是Hashtable的閾值,用於判斷是否需要調整Hashtable的容量。threshold的值="容量*加載因子"。
  loadFactor就是加載因子。 
  modCount是用來實現fail-fast機制的

 

第3部分 Hashtable主要方法

 HashTable的API對外提供了許多方法,這些方法能夠很好幫助我們操作HashTable,

3.1 put()

 首先我們先看put方法:將指定 key 映射到此哈希表中的指定 value。注意這里鍵key和值value都不可為空。

public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        /*
         * 確保key在table[]是不重復的
         * 處理過程:
         * 1、計算key的hash值,確認在table[]中的索引位置
         * 2、迭代index索引位置,如果該位置處的鏈表中存在一個一樣的key,則替換其value,返回舊值
         */
        Entry tab[] = table;
        //計算key的hash值
        int hash = hash(key);
        //確認該key的索引位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //迭代,尋找該key,替換
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }

        modCount++;
        //如果容器中的元素數量已經達到閥值,則進行擴容操作
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = hash(key);
index
= (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. Entry<K,V> e = tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; return null; }

 

 put方法的整個處理流程是:計算key的hash值,根據hash值獲得key在table數組中的索引位置,然后迭代該key處的Entry鏈表(我們暫且理解為鏈表),若該鏈表中存在一個這個的key對象,那么就直接替換其value值即可,否則在將改key-value節點插入該index索引位置處。如下:

首先我們假設一個容量為5的table,存在8、10、13、16、17、21。他們在table中位置如下:

 

 然后我們插入一個數:put(16,22),key=16在table的索引位置為1,同時在1索引位置有兩個數,程序對該“鏈表”進行迭代,發現存在一個key=16,這時要做的工作就是用newValue=22替換oldValue16,並將oldValue=16返回。

 

 在put(31,31),key=31所在的索引位置為3,並且在該鏈表中也沒有存在某個key=31的節點,所以就將該節點插入該鏈表的第一個位置。

 

在HashTabled的put方法中有兩個地方需要注意:

 1、HashTable的擴容操作,在put方法中,如果需要向table[]中添加Entry元素,會首先進行容量校驗,如果容量已經達到了閥值,HashTable就會進行擴容處理rehash(),如下:

 1 protected void rehash() {
 2         int oldCapacity = table.length;
 3         Entry<K,V>[] oldMap = table;
 4 
 5         // overflow-conscious code
 6         int newCapacity = (oldCapacity << 1) + 1;
 7         if (newCapacity - MAX_ARRAY_SIZE > 0) {
 8             if (oldCapacity == MAX_ARRAY_SIZE)
 9                 // Keep running with MAX_ARRAY_SIZE buckets
10                 return;
11             newCapacity = MAX_ARRAY_SIZE;
12         }
13         //新建一個size = newCapacity 的HashTable
14         Entry<K,V>[] newMap = new Entry[newCapacity];
15 
16         modCount++;
17         //重新計算閥值
18         threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
19         //重新計算hashSeed
20         boolean rehash = initHashSeedAsNeeded(newCapacity);
21 
22         table = newMap;
23         //將原來的元素拷貝到新的HashTable中
24         for (int i = oldCapacity ; i-- > 0 ;) {
25             for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
26                 Entry<K,V> e = old;
27                 old = old.next;
28 
29                 if (rehash) {
30                     e.hash = hash(e.key);
31                 }
32                 int index = (e.hash & 0x7FFFFFFF) % newCapacity;
33                 e.next = newMap[index];
34                 newMap[index] = e;
35             }
36         }
37     }

 

在這個rehash()方法中我們可以看到容量擴大兩倍+1,同時需要將原來HashTable中的元素一一復制到新的HashTable中,這個過程是比較消耗時間的,同時還需要重新計算hashSeed的,畢竟容量已經變了。這里對閥值啰嗦一下:比如初始值11、加載因子默認0.75,那么這個時候閥值threshold=8,當容器中的元素達到8時,HashTable進行一次擴容操作,容量 = 8 * 2 + 1 =17,而閥值threshold=17*0.75 = 13,當容器元素再一次達到閥值時,HashTable還會進行擴容操作,以此類推。

2、其實這里是我的一個疑問,在計算索引位置index時,HashTable進行了一個與運算過程(hash & 0x7FFFFFFF),至於為什么要與 0x7FFFFFFF, 那是hashtable 提供的hash算法, hashMap提供了不同的算法, 用戶如果要定義自己的算法也是可以的. 

下面是計算key的hash值,這里hashSeed發揮了作用。

 

private int hash(Object k) {
        return hashSeed ^ k.hashCode();
    }

 

3.2 get()

相對於put方法,get方法就會比較簡單,處理過程就是計算key的hash值,判斷在table數組中的索引位置,然后迭代鏈表,匹配直到找到相對應key的value,若沒有找到返回null。

 1 public synchronized V get(Object key) {
 2         Entry tab[] = table;
 3         int hash = hash(key);
 4         int index = (hash & 0x7FFFFFFF) % tab.length;
 5         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
 6             if ((e.hash == hash) && e.key.equals(key)) {
 7                 return e.value;
 8             }
 9         }
10         return null;
11     }

 

 3.3 putAll()

 putAll() 的作用是將“Map(t)”的中全部元素逐一添加到Hashtable中

public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

 

 3.4 clear()

 clear() 的作用是清空Hashtable。它是將Hashtable的table數組的值全部設為null

public synchronized void clear() {
        Entry tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

 

 3.5 contains() 和 containsValue()

 contains() 和 containsValue() 的作用都是判斷Hashtable是否包含“值(value)”

public synchronized boolean contains(Object value) {
        if (value == null) {
            throw new NullPointerException();
        }

        Entry tab[] = table;
        for (int i = tab.length ; i-- > 0 ;) {
            for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }

 

public boolean containsValue(Object value) {
        return contains(value);
    }

 

 3.6 containsKey()

 containsKey() 的作用是判斷Hashtable是否包含key

 

 1 public synchronized boolean containsKey(Object key) {
 2         Entry tab[] = table;
 3         int hash = hash(key);
 4         // 計算索引值,% tab.length 的目的是防止數據越界
 5         int index = (hash & 0x7FFFFFFF) % tab.length;
 6         // 找到“key對應的Entry(鏈表)”,然后在鏈表中找出“哈希值”和“鍵值”與key都相等的元素
 7         for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
 8             if ((e.hash == hash) && e.key.equals(key)) {
 9                 return true;
10             }
11         }
12         return false;
13     }

 

 3.7 elements()

 elements() 的作用是返回“所有value”的枚舉對象

 1 public synchronized Enumeration<V> elements() {
 2         return this.<V>getEnumeration(VALUES);
 3     }
 4     
 5 // 獲取Hashtable的枚舉類對象
 6 private <T> Enumeration<T> getEnumeration(int type) {
 7         if (count == 0) {
 8             return Collections.emptyEnumeration();
 9         } else {
10             return new Enumerator<>(type, false);
11         }
12     }

 

從中,我們可以看出:
(01) 若Hashtable的實際大小為0,則返回“空枚舉類”對象emptyEnumerator;
(02) 否則,返回正常的Enumerator的對象。(Enumerator實現了迭代器和枚舉兩個接口)

先看看emptyEnumerator對象是如何實現的:

 1 public static <T> Enumeration<T> emptyEnumeration() {
 2         return (Enumeration<T>) EmptyEnumeration.EMPTY_ENUMERATION;
 3     }
 4 
 5     private static class EmptyEnumeration<E> implements Enumeration<E> {
 6         static final EmptyEnumeration<Object> EMPTY_ENUMERATION
 7             = new EmptyEnumeration<>();
 8                 
 9         // 空枚舉類的hasMoreElements() 始終返回false
10         public boolean hasMoreElements() { return false; }
11         // 空枚舉類的nextElement() 拋出異常
12         public E nextElement() { throw new NoSuchElementException(); }
13     }

 

我們在來看看Enumeration類

Enumerator的作用是提供了“通過elements()遍歷Hashtable的接口” 和 “通過entrySet()遍歷Hashtable的接口”。因為,它同時實現了 “Enumerator接口”和“Iterator接口”。

 

  1 private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
  2         Entry[] table = Hashtable.this.table;
  3         int index = table.length;
  4         Entry<K,V> entry = null;
  5         Entry<K,V> lastReturned = null;
  6         int type;
  7 
  8         /**
  9          * Indicates whether this Enumerator is serving as an Iterator
 10          * or an Enumeration.  (true -> Iterator).
 11          */
 12         boolean iterator;
 13 
 14         // 在將Enumerator當作迭代器使用時會用到,用來實現fail-fast機制。
 15         protected int expectedModCount = modCount;
 16 
 17         Enumerator(int type, boolean iterator) {
 18             this.type = type;
 19             this.iterator = iterator;
 20         }
 21                 
 22                 // 從遍歷table的數組的末尾向前查找,直到找到不為null的Entry。
 23         public boolean hasMoreElements() {
 24             Entry<K,V> e = entry;
 25             int i = index;
 26             Entry[] t = table;
 27             /* Use locals for faster loop iteration */
 28             while (e == null && i > 0) {
 29                 e = t[--i];
 30             }
 31             entry = e;
 32             index = i;
 33             return e != null;
 34         }
 35                 
 36                 
 37            //從hasMoreElements() 和nextElement() 可以看出“Hashtable的elements()遍歷方式”
 38            // 首先,從后向前的遍歷table數組。table數組的每個節點都是一個單向鏈表(Entry)。
 39            // 然后,依次向后遍歷單向鏈表Entry。
 40         public T nextElement() {
 41             Entry<K,V> et = entry;
 42             int i = index;
 43             Entry[] t = table;
 44             /* Use locals for faster loop iteration */
 45             while (et == null && i > 0) {
 46                 et = t[--i];
 47             }
 48             entry = et;
 49             index = i;
 50             if (et != null) {
 51                 Entry<K,V> e = lastReturned = entry;
 52                 entry = e.next;
 53                 return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
 54             }
 55             throw new NoSuchElementException("Hashtable Enumerator");
 56         }
 57 
 58         // Iterator methods
 59         // 迭代器Iterator的判斷是否存在下一個元素,實際上,它是調用的hasMoreElements()
 60         public boolean hasNext() {
 61             return hasMoreElements();
 62         }
 63 
 64         public T next() {
 65             if (modCount != expectedModCount)
 66                 throw new ConcurrentModificationException();
 67             return nextElement();
 68         }
 69                 
 70         //首先,它在table數組中找出要刪除元素所在的Entry,然后,刪除單向鏈表Entry中的元素。
 71         public void remove() {
 72             if (!iterator)
 73                 throw new UnsupportedOperationException();
 74             if (lastReturned == null)
 75                 throw new IllegalStateException("Hashtable Enumerator");
 76             if (modCount != expectedModCount)
 77                 throw new ConcurrentModificationException();
 78 
 79             synchronized(Hashtable.this) {
 80                 Entry[] tab = Hashtable.this.table;
 81                 int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
 82 
 83                 for (Entry<K,V> e = tab[index], prev = null; e != null;
 84                      prev = e, e = e.next) {
 85                     if (e == lastReturned) {
 86                         modCount++;
 87                         expectedModCount++;
 88                         if (prev == null)
 89                             tab[index] = e.next;
 90                         else
 91                             prev.next = e.next;
 92                         count--;
 93                         lastReturned = null;
 94                         return;
 95                     }
 96                 }
 97                 throw new ConcurrentModificationException();
 98             }
 99         }
100     }
101     
102     
103     

 

 

 3.8 remove()

  remove() 的作用就是刪除Hashtable中鍵為key的元素

 1 public synchronized V remove(Object key) {
 2         Entry tab[] = table;
 3         int hash = hash(key);
 4         int index = (hash & 0x7FFFFFFF) % tab.length;
 5         // 找到“key對應的Entry(鏈表)”,然后在鏈表中找出要刪除的節點,並刪除該節點。
 6         for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
 7             if ((e.hash == hash) && e.key.equals(key)) {
 8                 modCount++;
 9                 if (prev != null) {
10                     prev.next = e.next;
11                 } else {
12                     tab[index] = e.next;
13                 }
14                 count--;
15                 V oldValue = e.value;
16                 e.value = null;
17                 return oldValue;
18             }
19         }
20         return null;
21     }

 

第4部分 Hashtable實現的Cloneable接口

 Hashtable實現了Cloneable接口,即實現了clone()方法。
clone()方法的作用很簡單,就是克隆一個Hashtable對象並返回。

 1 public synchronized Object clone() {
 2         try {
 3             Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
 4             t.table = new Entry[table.length];
 5             for (int i = table.length ; i-- > 0 ; ) {
 6                 t.table[i] = (table[i] != null)
 7                     ? (Entry<K,V>) table[i].clone() : null;
 8             }
 9             t.keySet = null;
10             t.entrySet = null;
11             t.values = null;
12             t.modCount = 0;
13             return t;
14         } catch (CloneNotSupportedException e) {
15             // this shouldn't happen, since we are Cloneable
16             throw new InternalError();
17         }
18     }

 

第5部分 Hashtable實現的Serializable接口

Hashtable實現java.io.Serializable,分別實現了串行讀取、寫入功能。

串行寫入函數就是將Hashtable的“總的容量,實際容量,所有的Entry”都寫入到輸出流中
串行讀取函數:根據寫入方式讀出將Hashtable的“總的容量,實際容量,所有的Entry”依次讀出

 1 /**
 2      * Save the state of the Hashtable to a stream (i.e., serialize it).
 3      *
 4      * @serialData The <i>capacity</i> of the Hashtable (the length of the
 5      *             bucket array) is emitted (int), followed by the
 6      *             <i>size</i> of the Hashtable (the number of key-value
 7      *             mappings), followed by the key (Object) and value (Object)
 8      *             for each key-value mapping represented by the Hashtable
 9      *             The key-value mappings are emitted in no particular order.
10      */
11     private void writeObject(java.io.ObjectOutputStream s)
12             throws IOException {
13         Entry<K, V> entryStack = null;
14 
15         synchronized (this) {
16             // Write out the length, threshold, loadfactor
17             s.defaultWriteObject();
18 
19             // Write out length, count of elements
20             s.writeInt(table.length);
21             s.writeInt(count);
22 
23             // Stack copies of the entries in the table
24             for (int index = 0; index < table.length; index++) {
25                 Entry<K,V> entry = table[index];
26 
27                 while (entry != null) {
28                     entryStack =
29                         new Entry<>(0, entry.key, entry.value, entryStack);
30                     entry = entry.next;
31                 }
32             }
33         }
34 
35         // Write out the key/value objects from the stacked entries
36         while (entryStack != null) {
37             s.writeObject(entryStack.key);
38             s.writeObject(entryStack.value);
39             entryStack = entryStack.next;
40         }
41     }
42 
43     /**
44      * Reconstitute the Hashtable from a stream (i.e., deserialize it).
45      */
46     private void readObject(java.io.ObjectInputStream s)
47          throws IOException, ClassNotFoundException
48     {
49         // Read in the length, threshold, and loadfactor
50         s.defaultReadObject();
51 
52         // Read the original length of the array and number of elements
53         int origlength = s.readInt();
54         int elements = s.readInt();
55 
56         // Compute new size with a bit of room 5% to grow but
57         // no larger than the original size.  Make the length
58         // odd if it's large enough, this helps distribute the entries.
59         // Guard against the length ending up zero, that's not valid.
60         int length = (int)(elements * loadFactor) + (elements / 20) + 3;
61         if (length > elements && (length & 1) == 0)
62             length--;
63         if (origlength > 0 && length > origlength)
64             length = origlength;
65 
66         Entry<K,V>[] newTable = new Entry[length];
67         threshold = (int) Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
68         count = 0;
69         initHashSeedAsNeeded(length);
70 
71         // Read the number of elements and then all the key/value objects
72         for (; elements > 0; elements--) {
73             K key = (K)s.readObject();
74             V value = (V)s.readObject();
75             // synch could be eliminated for performance
76             reconstitutionPut(newTable, key, value);
77         }
78         this.table = newTable;
79     }

 

 hashtable 的序列化和反序列化例子:

 

 1 /**
 2  * hashtale 序列化和反序列化
 3  * 
 4  * @ClassName: hashtable_test
 5  * @author Xingle
 6  * @date 2014-6-30 上午9:33:04
 7  */
 8 public class hashtable_test {
 9 
10     public static void main(String[] args) {
11 
12         Hashtable<String, String> ht = new Hashtable<>();
13         ht.put("1", "測試hashtable序列化");
14         ht.put("2", "天天見");
15         System.out.println("序列化前hashtable:"+ht);
16         new hashtable_test().serializable(ht);
17 
18     }
19 
20     private void serializable(Hashtable<String, String> ht_int) {
21 
22         try {
23             ObjectOutputStream out = new ObjectOutputStream(
24                     new FileOutputStream("test"));
25             out.writeObject(ht_int);
26             out.close();
27         } catch (FileNotFoundException e) {
28             e.printStackTrace();
29         } catch (IOException e) {
30             e.printStackTrace();
31         }
32 
33         try {
34             ObjectInputStream in = new ObjectInputStream(new FileInputStream(
35                     "test"));
36             Hashtable<String, String> ht_out = (Hashtable<String, String>) in.readObject();
37             System.out.println("反序列化后hashtable:"+ht_out);
38         } catch (FileNotFoundException e) {
39             e.printStackTrace();
40         } catch (IOException e) {
41             e.printStackTrace();
42         } catch (ClassNotFoundException e) {
43             e.printStackTrace();
44         }
45 
46     }
47 
48 }

執行結果:

序列化前hashtable:{2=天天見, 1=測試hashtable序列化}
反序列化后hashtable:{2=天天見, 1=測試hashtable序列化}

 

 

 


 

參考:java提高篇(二五)-----HashTable

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM