Hashtable 提供的功能
- Hashtable是一個線程安全的Map,其線程安全是通過在各個方法上加上synchronized關鍵字實現的,即:該類只能被一個線程所使用,其他調用該類時會阻塞等待;
- 實現了哈希表,映射key到value;
- key和value都不能爲null,key類型必須實現hashCode()和equals()方法;
- put(K k,V v);
- get(K k,V v);
Hashtable沒有實現hash衝突的解決方案,衝突需要按自己的邏輯實現,它只提供了哈希表自動擴容的功能;- 出現hash衝突時,採用單向鏈表來儲存衝突的元素。
Hashtable 涉及到的概念
- 初始化容量:key數組初始長度,默認值是11
- 負載係數(load factor):即到達容量的百分比時,哈希表就會重新hash到一個新容量的哈希表中,取值範圍是:<1.0 的百分數 默認取值是.75(75%,當加入的鍵值對數量達到初始容量的75%時,繼續加入的話會重新hash到一個新的容量的哈希表中)
- 閾(yù)值:threshold=初始化容量*0.75和Integer.MAX_VALUE-8(注:Integer.MAX_VALUE=0x7FFFFFFF) 中較小值
- 重新Hash:加入元素時,當數組大小大於等於閾值時,key數組的容量會左移2位後+1即:原容量*4+1,然後按新的容量hash後放入新的數組中。
- 哈希函數:(key.hashCode() & 0x7FFFFFFF) % key數組現在的長度
Hashtable常用方法分析
- put(K k,V v); 添加元素到哈希表時,判斷元素是否存在,如果存在(key的hash相同並且值也相同)的話,就會覆蓋掉原來的元素,並返回原來的值;如果不存在的話,就會直接新建一個元素,若產生hash衝突則將舊元素鏈接到新元素的尾部。
- get(K k); 獲取元素時,先根據key的hash獲取到對應key數組的下標,獲取對應的元素(因爲數組元素的值是一個單鏈表,所以如果當前的值不匹配時就需要判斷鏈表的下一個元素)。
處理 Hash衝突
Hashtable 處理 Hash衝突 時通過單鏈表。。
涉及的基本概念
- 位運算
- 數組
- 哈希表
- 單鏈表
- Java transient 關鍵字原理
- Java 序列化、反序列化以及自定義序列化、反序列話邏輯(writeObject(ObjectOutputStream o)和readObject(ObjectInputStream i))
存在未理解之處
Hashtable的count字段是什麼時候初始化的?從賦值來看是通過readObject()方法來實現的。但是具體實現需要回去取研讀下《Java編程思想》序列化章節內容。
private transient int count;
未理解之處的解答
- Hashtable的count字段是什麼時候初始化的?因爲該變量是類變量編譯的時候會自動賦值。可以總結下Java各種類型的變量。
模擬hash衝突
可以根據hash函數(key.hashCode() & 0x7FFFFFFF) % key數組現在的長度
來模擬hash衝突,只要key的hashCode是相同但是又不equals()的就是Hash衝突。如下實例:
public class MyKey implements Serializable {
private int i;
public MyKey(int i) {
this.i = i;
}
[@Override](https://my.oschina.net/u/1162528)
public int hashCode() {
if (i % 2 == 0) {
return 1;
} else {
return 2;
}
}
}
Hashtable處理hash衝突
如下實例:
public static void main(String[] args) {
Hashtable<MyKey, Integer> map = new Hashtable<>();
for (int i = 0; i < 11; i++) {
map.put(new MyKey(i), i);
}
map.get(new MyKey(1));
}
如何在IDEA調試模式下查看儲存結構?
Hashtable在IDEA下的默認視圖:
如何查看對象視圖?如下圖操作:
對象視圖如下
從如上對象視圖可以看出Hashtable的table字段的具體存儲方式:
table數組中有兩個元素,一個是MyKey.i=10
,一個是Mykey.i=9
,按如上查看對象視圖的方法查看這兩個元素的對象視圖查看java.util.Hashtable.Entry#next
屬性,可以看到MyKey.i=10
的next屬性值是MyKey.i=8
...
各元素的具體結構如下:
MyKey.i=10.next -> MyKey.i=8
MyKey.i=8.next -> MyKey.i=0
MyKey.i=0.next -> MyKey.i=2
MyKey.i=2.next -> MyKey.i=4
MyKey.i=4.next -> MyKey.i=6
MyKey.i=6.next -> null
MyKey.i=9.next -> MyKey.i=1
MyKey.i=1.next -> MyKey.i=3
MyKey.i=3.next -> MyKey.i=5
MyKey.i=5.next -> MyKey.i=7
MyKey.i=7.next -> null
爲什麼順序不是按加入的順序的呢,而是一部分到過來的?
因爲Hashtable的key數組默認大小是11,當加入11個元素時,會自動擴容,在加入第8個元素時會rehash一次,rehash時是將新哈希表中的元素作爲後面加入元素的next的,所有就會出現部分元素順序相反。