Java Hashtable 涉及的數據結構、實現及衝突處理 頂 原

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下的默認視圖:

Java-Hashtable-data-structure-default-view

如何查看對象視圖?如下圖操作:

Java-Hashtable-data-structure

對象視圖如下

Java-Hashtable-data-structure-object-view

從如上對象視圖可以看出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的,所有就會出現部分元素順序相反。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章