Java常見(jiàn)比較一
1.線程是否安全?
HashMap 是非線程安全的,HashTable 是線程安全的;HashTable 內(nèi)部的方法基本都經(jīng)過(guò)synchronized修飾。(如果你要保證線程安全,可以使用ConcurrentHashmap)
2.效率
因?yàn)榫€程安全的問(wèn)題,Hashmap 要比 HashTable 效率高一點(diǎn)。不建議使用 HashTable。
3.對(duì) Null key 和 Null value 的支持
HashMap 中,null 可以作為鍵,這樣的鍵只有一個(gè),可以有一個(gè)或者多個(gè)鍵所對(duì)應(yīng)額的值為 null。但是在 HashTable 中鍵值為null,將報(bào)NullPointerException。
4.初始容量大小和每次擴(kuò)充容量大小的不同
① 創(chuàng)建時(shí)如果不指定容量初始值,Hashtable 默認(rèn)值的初始大小為11,之后每次擴(kuò)充,容量變?yōu)樵瓉?lái)的 2n+1。HashMap 默認(rèn)的初始值為16,之后每次擴(kuò)充,容量變?yōu)樵瓉?lái)的 2 倍。
②創(chuàng)建時(shí)如果給定了容量初始值,那么 Hashtable 會(huì)直接使用你給定的大小,而HashMap 會(huì)將其擴(kuò)充為 2 的冪次方大小。也就是說(shuō) HashMap 總是使用 2 的冪作為哈希表的大小。
5.底層數(shù)據(jù)結(jié)構(gòu)
JDK1.8 以后的 HashMap 在解決哈希沖突時(shí)有了較大的變化,當(dāng)鏈表長(zhǎng)度大于閾值(默認(rèn)為8)時(shí),將鏈表轉(zhuǎn)化為紅黑樹(shù),以減少搜索時(shí)間。Hashtable 沒(méi)有這樣的機(jī)制。
HashMap 中帶有初始容量的構(gòu)造函數(shù):
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);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
下面這個(gè)方法保證了 HashMap總是使用 2 的冪作為哈希表的大?。?/p>
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
HashMap的長(zhǎng)度為什么是 2 的冪次方?
為了能讓 HashMap存取搞笑,盡量較少碰撞,也就是要盡量把數(shù)據(jù)分配均勻。Hash 值的范圍值 -2147483648 到 2147483647,前面加起來(lái)大概 40 億長(zhǎng)度的數(shù)組,內(nèi)存是放不下的。所以這個(gè)散列值是不能直接拿來(lái)用的。要先對(duì)數(shù)組的長(zhǎng)度取模運(yùn)算,得到的余數(shù)才能用要存放的位置也就是對(duì)應(yīng)的數(shù)組下標(biāo)。這個(gè)數(shù)組下標(biāo)的計(jì)算方法是“(n-1)& hash”。(n代表數(shù)組長(zhǎng)度)這也就解釋了 HashMap 的長(zhǎng)度為什么是 2 的冪次方。
這個(gè)算法應(yīng)該如何設(shè)計(jì)
我們首先可能會(huì)想到采用%取余的操作來(lái)實(shí)現(xiàn)。但是,重點(diǎn)來(lái)了:“取余(%)操作中如果除數(shù)是 2 的冪次則等價(jià)于與其除數(shù)減一的與(&)操作(也就是說(shuō) hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方)” 并且采用二進(jìn)制位操作 &,相對(duì)于 % 能夠提高運(yùn)算效率,這就解釋了 HashMap 的長(zhǎng)度為什么是 2 的冪次方。