哈希表是一種能夠進行快速查找且能夠支持高效插入的數據結構,JAVA已經有多個不同的類實現了哈希表,在日常應用中,我們經常會使用哈希表存儲一些數據,通過get和push方法實現數據的獲取和存儲。
先簡單看看一些簡單的區別
- 大小限制
HashTable和HashMap都有默認的初始化大小,hashTable的默認大小是 11*0.75,hashMap的默認大小是16
QQ截圖20170215200405.png
- HashTable 繼承與Distionary類,使用Entry的數組存儲數據,HashMap繼承AbstractMap類,使用Entry數組存儲數據,可以看到的是,兩者的基礎bean都是一樣的,都包含hash、key、value、next,從數據結構來看,這里應該用到的就是拉鏈法,拉鏈法后面會進行詳細介紹,簡單來說,就是在沖突的時候,通過鏈表來處理沖突。
2.png
- HashTable操作entry數組的時候會使用JAVA的同步關鍵字,防止多線程的時候,entry數組溢出,而HashMap則無使用,HashTable是線程安全的,而HashMap不是
Paste_Image.png
JDK1.7使用了拉鏈法
Paste_Image.png
Paste_Image.png
- 簡單來說,拉鏈法就是數組里面的每一個元素都會作為一個鏈表的頭結點,當通過put方法將數據放進哈希表時,哈希算法計算到的數組位置如果是相同的元素,會組建成鏈表,從而解決沖突。
- 拉鏈法優化性能,都是通過擴大容量來減少沖突,為什么呢?因為每一條鏈表的長度都會縮短,通過哈希算法計算出數組位置一樣的可能性降低了。
void transfer(Entry[] newTable) {
Entry[] src = table; //src引用了舊的Entry數組
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) { //遍歷舊的Entry數組
Entry<K, V> e = src[j]; //取得舊Entry數組的每個元素
if (e != null) {
src[j] = null;//釋放舊Entry數組的對象引用(for循環后,舊的Entry數組不再引用任何對象)
do {
Entry<K, V> next = e.next;
int i = indexFor(e.hash, newCapacity); //!!重新計算每個元素在數組中的位置
e.next = newTable[i]; //標記[1]
newTable[i] = e; //將元素放在數組上
e = next; //訪問下一個Entry鏈上的元素(訪問鏈表的下一個元素,鏈表頭結點就是newTable[i])
} while (e != null);
}
}
}
HashTable是如何解決線程安全的?
HashTable類的實例,對于不同線程的臨界區是entry數組,不同線程之間對該數組進行插入、更新、刪除操作可能會造成線程安全問題。由于entry數組里面有很多個元素,如果想對entry數組加synchronized關鍵字顯然不可能,因為你不知道,每一個線程到底什么時候才會對哪個元素進行操作。HashTable使用了最簡單的辦法,那就是將數組作為一個整體,對于訪問該數組的方法,基本上都加了synchronized關鍵字,。但由于這樣鎖的粒度會比較大,性能也就沒有concurrentHashMap快了。