大师兄咚咚咚

浅析Hashmap和Hashtable

一、Hashmap不是线程安全的,而Hashtable是线程安全的

通过查看源码可以发现,hashmap类中的方法无synchronized关键字,而hashtable类中的方法有synchronized关键字修饰。

二、Hashmap允许key和value为null,Hashtable则不允许

 hashmap允许key和value为null,当key为null时会将其置于table[0]的链表中进行存储;而hashtable则会抛出异常。

 1 以下代码及注释来自java.util.HashTable
 2  
 3 public synchronized V put(K key, V value) {
 4  
 5     // 如果value为null,抛出NullPointerException
 6     if (value == null) {
 7         throw new NullPointerException();
 8     }
 9  
10     // 如果key为null,在调用key.hashCode()时抛出NullPointerException
11  
12     // ...
13 }
14  
15  
16 以下代码及注释来自java.util.HasMap
17  
18 public V put(K key, V value) {
19     if (table == EMPTY_TABLE) {
20         inflateTable(threshold);
21     }
22     // 当key为null时,调用putForNullKey特殊处理
23     if (key == null)
24         return putForNullKey(value);
25     // ...
26 }
27  
28 private V putForNullKey(V value) {
29     // key为null时,放到table[0]也就是第0个bucket中
30     for (Entry<K,V> e = table[0]; e != null; e = e.next) {
31         if (e.key == null) {
32             V oldValue = e.value;
33             e.value = value;
34             e.recordAccess(this);
35             return oldValue;
36         }
37     }
38     modCount++;
39     addEntry(0, null, value, 0);
40     return null;
41 }

 

三、索引的计算方式不同

hashmap采用“与运算”,而hashtable采用取模运算,相比较而言hashmap的计算效率更高。

/*---------hashmap---------*/
public V put(K key, V value) {
        //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(24=16)
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
       //如果key为null,存储位置为table[0]或table[0]的冲突链上
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
        int i = indexFor(hash, table.length);//获取在table中的实际位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        //如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
        addEntry(hash, key, value, i);//新增一个entry
        return null;
    }    

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

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

四、初始容量及扩容算法不同

hashmap的初始容量是16,而hashtable的初始容量为11;hashmap采用的算法是:2*oldSize,而hashtable采用的算法是2*oldSize+1;

以下代码及注释来自java.util.HashTable
 
// 哈希表默认初始大小为11
public Hashtable() {
    this(11, 0.75f);
}
 
protected void rehash() {
    int oldCapacity = table.length;
    Entry<K,V>[] oldMap = table;
 
    // 每次扩容为原来的2n+1
    int newCapacity = (oldCapacity << 1) + 1;
    // ...
}
 
 
以下代码及注释来自java.util.HashMap
 
// 哈希表默认初始大小为2^4=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 
void addEntry(int hash, K key, V value, int bucketIndex) {
    // 每次扩充为原来的2n 
    if ((size >= threshold) && (null != table[bucketIndex])) {
       resize(2 * table.length);
}

 

the ending! 

posted on 2018-03-29 10:58  大师兄咚咚咚  阅读(121)  评论(0编辑  收藏  举报

导航