java集合(3)——HashMap和HashTable的區別

1.null key & null value

//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);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
//Hashtable
    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)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

從代碼上看HashMap允許null值,而Hashtable遇見null會拋 IllegalArgumentException(“Illegal Load: “+loadFactor)異常

2.初始值和擴容大小

public Hashtable() {
    this(11, 0.75f);
}
protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // 每次擴充爲原來的2n+1
        int newCapacity = (oldCapacity << 1) + 1;
        //......
}
// 哈希表默認初始大小爲2^4=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // 擴充爲原來的2n
                //......
}

可以看到HashTable默認的初始大小爲11,之後每次擴充爲原來的2n+1。HashMap默認的初始化大小爲16,之後每次擴充爲原來的2倍。還有我沒列出代碼的一點,就是如果在創建時給定了初始化大小,那麼HashTable會直接使用你給定的大小,而HashMap會將其擴充爲2的冪次方大小。

3.線程安全

我們說HashTable是同步的,HashMap不是,也就是說HashTable在多線程使用的情況下,不需要做額外的同步,而HashMap則不行。那麼HashTable是怎麼做到的呢?

public synchronized V get(Object key) {
    Entry tab[] = table;
    int hash = hash(key);
    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)) {
            return e.value;
        }
    }
    return null;
}

public Set<K> keySet() {
    if (keySet == null)
        keySet = Collections.synchronizedSet(new KeySet(), this);
    return keySet;
}

可以看到,也比較簡單,就是公開的方法比如get都使用了synchronized描述符。而遍歷視圖比如keySet都使用了Collections.synchronizedXXX進行了同步包裝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章