程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java集合源碼剖析:Hashtable源碼剖析

Java集合源碼剖析:Hashtable源碼剖析

編輯:關於JAVA

Hashtable簡介

Hashtable同樣是基於哈希表實現的,同樣每個元素是一個key-value對,其內部也是通過單鏈表解決沖突問題,容量不足(超過了閥值)時,同樣會自動增長。

Hashtable也是JDK1.0引入的類,是線程安全的,能用於多線程環境中。

Hashtable同樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。

HashTable源碼剖析

Hashtable的源碼的很多實現都與HashMap差不多,源碼如下(加入了比較詳細的注釋):

package java.util;    
import java.io.*;    
       
public class Hashtable<K,V>    
    extends Dictionary<K,V>    
    implements Map<K,V>, Cloneable, java.io.Serializable {    
       
    // 保存key-value的數組。    
    // Hashtable同樣采用單鏈表解決沖突,每一個Entry本質上是一個單向鏈表    
    private transient Entry[] table;    
       
    // Hashtable中鍵值對的數量    
    private transient int count;    
       
    // 阈值,用於判斷是否需要調整Hashtable的容量(threshold = 容量*加載因子)    
    private int threshold;    
       
    // 加載因子    
    private float loadFactor;    
       
    // Hashtable被改變的次數,用於fail-fast機制的實現    
    private transient int modCount = 0;    
       
    // 序列版本號    
    private static final long serialVersionUID = 1421746759512286392L;    
       
    // 指定“容量大小”和“加載因子”的構造函數    
    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 = new Entry[initialCapacity];    
        threshold = (int)(initialCapacity * loadFactor);    
    }    
       
    // 指定“容量大小”的構造函數    
    public Hashtable(int initialCapacity) {    
        this(initialCapacity, 0.75f);    
    }    
       
    // 默認構造函數。    
    public Hashtable() {    
        // 默認構造函數,指定的容量大小是11;加載因子是0.75    
        this(11, 0.75f);    
    }    
       
    // 包含“子Map”的構造函數    
    public Hashtable(Map<? extends K, ? extends V> t) {    
        this(Math.max(2*t.size(), 11), 0.75f);    
        // 將“子Map”的全部元素都添加到Hashtable中    
        putAll(t);    
    }    
       
    public synchronized int size() {    
        return count;    
    }    
       
    public synchronized boolean isEmpty() {    
        return count == 0;    
    }    
       
    // 返回“所有key”的枚舉對象    
    public synchronized Enumeration<K> keys() {    
        return this.<K>getEnumeration(KEYS);    
    }    
       
    // 返回“所有value”的枚舉對象    
    public synchronized Enumeration<V> elements() {    
        return this.<V>getEnumeration(VALUES);    
    }    
       
    // 判斷Hashtable是否包含“值(value)”    
    public synchronized boolean contains(Object value) {    
        //注意,Hashtable中的value不能是null,    
        // 若是null的話,拋出異常!    
        if (value == null) {    
            throw new NullPointerException();    
        }    
       
        // 從後向前遍歷table數組中的元素(Entry)    
        // 對於每個Entry(單向鏈表),逐個遍歷,判斷節點的值是否等於value    
        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);    
    }    
       
    // 判斷Hashtable是否包含key    
    public synchronized boolean containsKey(Object key) {    
        Entry tab[] = table;    
        //計算hash值,直接用key的hashCode代替  
        int hash = key.hashCode();      
        // 計算在數組中的索引值   

        int index = (hash & 0x7FFFFFFF) % tab.length;    
        // 找到“key對應的Entry(鏈表)”,然後在鏈表中找出“哈希值”和“鍵值”與key都相等的元素    
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {    
            if ((e.hash == hash) && e.key.equals(key)) {    
                return true;    
            }    
        }    
        return false;    
    }    
       
    // 返回key對應的value,沒有的話返回null    
    public synchronized V get(Object key) {    
        Entry tab[] = table;    
        int hash = key.hashCode();    
        // 計算索引值,    
        int index = (hash & 0x7FFFFFFF) % tab.length;    
        // 找到“key對應的Entry(鏈表)”,然後在鏈表中找出“哈希值”和“鍵值”與key都相等的元素    
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {    
            if ((e.hash == hash) && e.key.equals(key)) {    
                return e.value;    
            }    
        }    
        return null;    
    }    
       
    // 調整Hashtable的長度,將長度變成原來的2倍+1   
    protected void rehash() {    
        int oldCapacity = table.length;    
        Entry[] oldMap = table;    
       
        //創建新容量大小的Entry數組  
        int newCapacity = oldCapacity * 2 + 1;    
        Entry[] newMap = new Entry[newCapacity];    
       
        modCount++;    
        threshold = (int)(newCapacity * loadFactor);    
        table = newMap;    
              
        //將“舊的Hashtable”中的元素復制到“新的Hashtable”中  
        for (int i = oldCapacity ; i-- > 0 ;) {    
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {    
                Entry<K,V> e = old;    
                old = old.next;    
                //重新計算index  
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;    
                e.next = newMap[index];    
                newMap[index] = e;    
            }    
        }    
    }    
       
    // 將“key-value”添加到Hashtable中    
    public synchronized V put(K key, V value) {    
        // Hashtable中不能插入value為null的元素!!!    
        if (value == null) {    
            throw new NullPointerException();    
        }    
       
        // 若“Hashtable中已存在鍵為key的鍵值對”,    
        // 則用“新的value”替換“舊的value”    
        Entry tab[] = table;    
        int hash = key.hashCode();    
        int index = (hash & 0x7FFFFFFF) % tab.length;    
        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;    
                }    
        }    
       
        // 若“Hashtable中不存在鍵為key的鍵值對”,  
        // 將“修改統計數”+1    
        modCount++;    
        //  若“Hashtable實際容量” > “阈值”(阈值=總的容量 * 加載因子)    
        //  則調整Hashtable的大小    
        if (count >= threshold) {  
            rehash();    
       
            tab = table;    
            index = (hash & 0x7FFFFFFF) % tab.length;    
        }    
       
        //將新的key-value對插入到tab[index]處(即鏈表的頭結點)  
        Entry<K,V> e = tab[index];           
        tab[index] = new Entry<K,V>(hash, key, value, e);    
        count++;    
        return null;    
    }    
       
    // 刪除Hashtable中鍵為key的元素    
    public synchronized V remove(Object key) {    
        Entry tab[] = table;    
        int hash = key.hashCode();    
        int index = (hash & 0x7FFFFFFF) % tab.length;    
              
        //從table[index]鏈表中找出要刪除的節點,並刪除該節點。  
        //因為是單鏈表,因此要保留帶刪節點的前一個節點,才能有效地刪除節點  
        for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {    
            if ((e.hash == hash) && e.key.equals(key)) {    
                modCount++;    
                if (prev != null) {    
                    prev.next = e.next;    
                } else {    
                    tab[index] = e.next;    
                }    
                count--;    
                V oldValue = e.value;    
                e.value = null;    
                return oldValue;    
            }    
        }    
        return null;    
    }    
       
    // 將“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());    
    }    
       
    // 清空Hashtable    
    // 將Hashtable的table數組的值全部設為null 
// 本欄目

1、二者的存儲結構和解決沖突的方法都是相同的。

2、HashTable在不指定容量的情況下的默認容量為11,而HashMap為16,Hashtable不要求底層數組的容量一定要為2的整數次冪,而HashMap則要求一定為2的整數次冪。

3、Hashtable中key和value都不允許為null,而HashMap中key和value都允許為null(key只能有一個為null,而value則可以有多個為null)。但是如果在Hashtable中有類似put(null,null)的操作,編譯同樣可以通過,因為key和value都是Object類型,但運行時會拋出NullPointerException異常,這是JDK的規范規定的。我們來看下ContainsKey方法和ContainsValue的源碼:

// 判斷Hashtable是否包含“值(value)”    
 public synchronized boolean contains(Object value) {    
     //注意,Hashtable中的value不能是null,
	 // 本欄目

4、Hashtable擴容時,將容量變為原來的2倍加1,而HashMap擴容時,將容量變為原來的2倍。

5、Hashtable計算hash值,直接用key的hashCode(),而HashMap重新計算了key的hash值,Hashtable在求hash值對應的位置索引時,用取模運算,而HashMap在求位置索引時,則用與運算,且這裡一般先用hash&0x7FFFFFFF後,再對length取模,&0x7FFFFFFF的目的是為了將負的hash值轉化為正值,因為hash值有可能為負數,而&0x7FFFFFFF後,只有符號外改變,而後面的位都不變。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved