HashTable本身和hashMap差距不大,看了幾個(gè)hashTable的內(nèi)部方法實(shí)現(xiàn)榜晦,發(fā)現(xiàn)內(nèi)部方法沒(méi)有上鎖堡赔,但是用public修飾的方法全部用synchronize加上了方法鎖港庄,這樣是極其消耗性能的锰茉,好暇屋,我們來(lái)看幾個(gè)內(nèi)部方法的實(shí)現(xiàn)。
rehash()
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 將容量擴(kuò)大為原來(lái)的兩倍+1
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
//如果等于最大容量洞辣,則直接返回,如果大于最大容量昙衅,則將容量賦值成最大容量
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
//新數(shù)組
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
//這個(gè)地方有點(diǎn)意思扬霜,跟hashMap和ConcurrentHashmap的實(shí)現(xiàn)不一樣
//前兩者是使用hash值與舊數(shù)組長(zhǎng)度進(jìn)行與操作,然后分成兩條鏈而涉,直接放到相應(yīng)位置
//而hashTable是從前面插入著瓶,也就是每插入一個(gè)節(jié)點(diǎn),就將這個(gè)節(jié)點(diǎn)的next指向原有的首節(jié)點(diǎn)
//然后插入節(jié)點(diǎn)充當(dāng)當(dāng)前位置的首節(jié)點(diǎn)
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
//這個(gè)地方有點(diǎn)疑問(wèn)
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
在數(shù)據(jù)遷移過(guò)程中啼县,我們可以看到hashTable是通過(guò)取余來(lái)獲取位置的材原,這個(gè)效率肯定不如hashmap,還有一個(gè)地方是hash和0x7fffffff進(jìn)行了與操作季眷,這個(gè)三個(gè)集合都有點(diǎn)不同余蟹,hashmap是先將hash值無(wú)符號(hào)右移16位然后再跟自身進(jìn)行異或操作,這個(gè)是為了減少hash沖突子刮,但是在ConcurrentHashmap中威酒,它的hash值計(jì)算在這個(gè)基礎(chǔ)上還跟0x7fffffff進(jìn)行了與操作窑睁,而hashtable沒(méi)有高16和低16位的異或操作,直接跟0x7fffffff進(jìn)行與操作葵孤。
總結(jié)一下:
1担钮、hashmap跟hashtable和ConcurrentHashmap的區(qū)別在于,后兩者都是線程安全的集合尤仍,然后他們都跟0x7fffffff進(jìn)行了與操作
2箫津、hashTable的位置計(jì)算直接通過(guò)取余的方式,在位置選擇上的性能上會(huì)低于hashmap和concurrentHashmap
3宰啦、ConcurrentHashmap中有個(gè)數(shù)據(jù)遷移的地方比較高明苏遥,在分成兩條鏈進(jìn)行插入的時(shí)候,它要對(duì)原有的鏈進(jìn)行遍歷绑莺,在遍歷的過(guò)程中發(fā)現(xiàn)可以重用的節(jié)點(diǎn)暖眼,然后不用再二次創(chuàng)建這些節(jié)點(diǎn),在使用中可能會(huì)大大提高性能夠纺裁,因?yàn)檫@個(gè)類是處理高并發(fā)的集合诫肠。
addEntry
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
//如果數(shù)組長(zhǎng)度大于閾值,則進(jìn)行擴(kuò)容處理
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
//獲取位置
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
//插入節(jié)點(diǎn)欺缘,也是一樣從鏈表頭插入
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
這個(gè)方法也比較簡(jiǎn)單栋豫,跟前面的rehash一樣,插入新節(jié)點(diǎn)是從鏈表頭插入的
put
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;
}
這個(gè)方法之所以拿出來(lái)講谚殊,我們看第一條判斷語(yǔ)句丧鸯,value==null的判斷,也就是說(shuō)hashtable不支持value為空嫩絮,但是支持key為空丛肢,hashmap既支持key也支持value為空,而ConcurrentHashMap是都不支持二者為空的情況剿干,這個(gè)也是三個(gè)集合類的區(qū)別之一蜂怎。
為什么要這樣規(guī)定key或者value不能為空呢?(網(wǎng)上感覺(jué)比較合理的解釋如下)
ConcurrentHashmap和Hashtable都是支持并發(fā)的置尔,這樣會(huì)有一個(gè)問(wèn)題杠步,當(dāng)你通過(guò)get(k)獲取對(duì)應(yīng)的value時(shí),如果獲取到的是null時(shí)榜轿,你無(wú)法判斷幽歼,它是put(k,v)的時(shí)候value為null,還是這個(gè)key從來(lái)沒(méi)有做過(guò)映射谬盐。HashMap是非并發(fā)的甸私,可以通過(guò)contains(key)來(lái)做這個(gè)判斷。而支持并發(fā)的Map在調(diào)用m.contains(key)和m.get(key),m可能已經(jīng)不同了飞傀。
clone
//Creates a shallow copy of this hashtable. All the structure of the
// hashtable itself is copied, but the keys and values are not cloned.
//This is a relatively expensive operation
public synchronized Object clone() {
try {
Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
t.table = new Entry<?,?>[table.length];
for (int i = table.length ; i-- > 0 ; ) {
t.table[i] = (table[i] != null)
? (Entry<?,?>) table[i].clone() : null;
}
t.keySet = null;
t.entrySet = null;
t.values = null;
t.modCount = 0;
return t;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
看到方法上的注釋可以知道這個(gè)類跟hashmap一樣都是淺拷貝的颠蕴,key和value值不拷貝泣刹,而相比于此,ConcurrentHashMap是沒(méi)有提供拷貝方法的犀被,也就是不能進(jìn)行拷貝椅您。