關於Java編程,Hashtable、HashMap和TreeMap有什麼區別?

關於Java編程,Hashtable、HashMap和TreeMap有什麼區別?

典型的回答

Hashtable、HashMap、TreeMap都是最常見的Map接口的實現,是以鍵值對的形式存儲和操作數據的容器類型。

Hashtable是早期Java類庫提供的一個哈希表實現,本身是線程安全的,不支持null鍵和值。由於線程安全導致的性能開銷,所以已經很少被推薦使用。

HashMap是應用更加廣泛的哈希表實現,行為上大致與Hashtable一致,主要區別在於HashMap不是線程安全的,且支持null鍵和值等。通常情況下,HashMap進行put或者get操作,可以達到常數時間的性能,所以它是絕大部分利用鍵值對存取場景的首選。

TreeMap則是基於紅黑樹的一種提供順序訪問的Map,和HashMap不同,它的get、put、remove之類操作都是O(log(n))的時間複雜度,具體順序可以由指定的Comparator來決定,或者根據鍵的自然順序來判斷。

知識擴展

1、Map整體結構

Map被包括在Java集合框架裡,但其本身並不是狹義上的集合類型(Collection)。

關於Java編程,Hashtable、HashMap和TreeMap有什麼區別?

Hashtable比較特別,作為類似Vector、Stack的早期集合相關類型,它是擴展了Dictionary類,結構上與HashMap之類明顯不同。

大部分使用Map的場景,通常就是放入、訪問或者刪除,而對順序沒有特別要求。

HashMap在這種情況下基本上是最好的選擇。HashMap的性能表現非常依賴於哈希碼的有效性,請務必掌握hashCode和equals的一些基本約定:

01

equals相等,hashCode一定要相等。

02

重寫了hashCode也要重寫equals。

03

hashCode需要保持一致性,狀態改變返回的哈希值仍然要一致。

04

equals要滿足對稱、反射與傳遞性。

還有部分使用Map的場景中,對於鍵值對的順序是有要求的。這時就應該考慮使用LinkedHashMap和TreeMap。

2、LinkedHashMap vs. TreeMap

雖然LinkedHashMap和TreeMap都可以保證某種順序,但二者還是非常不同的。

TreeMap是利用紅黑樹來實現的。樹中的每個節點的值,都會大於或等於它的左子樹中的所有節點的值,並且小於或等於它的右子樹中的所有節點的值。它實現了SortedMap接口,能夠對保存的記錄根據鍵進行排序。也就是說TreeMap的整體順序是由鍵的順序關係決定的,通過Comparator或Comparable(自然順序)來決定。類似於hashCode和equals的約定,為了避免模稜兩可的情況,自然順序同樣需要符合一個約定:compareTo的返回值需要和equals一致。

LinkedHashMap通常提供的是遍歷順序符合插入順序。它的實現是通過為條目(鍵值對)維護一個雙向鏈表。注意,通過特定的構造函數,我們可以創建反映訪問順序的實例,所謂的put、get、compute等,都算作“訪問”。

這種行為適用於一些特定應用場景,例如,我們構建一個空間佔用敏感的資源池,希望可以自動將最不常被訪問的對象釋放掉,這就可以利用LinkedHashMap提供的機制來實現。參考以下示例:

public class LinkedHashMapSample {

public static void main(String[] args) {

LinkedHashMap map = new LinkedHashMap(16, 0.75f, true) {

@Override

protected boolean removeEldestEntry(Map.Entry eldest) {

// 實現自定義的刪除策略,否則行為就和普通Map沒有區別。

return size() > 3;

}

};

map.put("Project1", "Apple");

map.put("Project2", "Banana");

map.put("Project3", "Cherry");

map.forEach((k, v) -> {

System.out.println(k + ":" + v);

});

// 模擬訪問

map.get("Project2");

map.get("Project2");

map.get("Project3");

System.out.println();

map.forEach((k, v) -> {

System.out.println(k + ":" + v);

});

// 觸發刪除

map.put("Project4", "Mango");

System.out.println();

map.forEach((k, v) -> {

System.out.println(k + ":" + v);

});

}

}

輸出如下:

Project1:Apple

Project2:Banana

Project3:Cherry

Project1:Apple

Project2:Banana

Project3:Cherry

Project2:Banana

Project3:Cherry

Project4:Mango


分享到:


相關文章: