深入理解HashMap和TreeMap的区别
文章目錄
- 簡介
- HashMap和TreeMap本質區別
- 排序區別
- Null值的區別
- 性能區別
- 共同點
深入理解HashMap和TreeMap的區別
簡介
HashMap和TreeMap是Map家族中非常常用的兩個類,兩個類在使用上和本質上有什么區別呢?本文將從這兩個方面進行深入的探討,希望能揭露其本質。
HashMap和TreeMap本質區別
先看HashMap的定義:
public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable再看TreeMap的定義:
public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable從類的定義來看,HashMap和TreeMap都繼承自AbstractMap,不同的是HashMap實現的是Map接口,而TreeMap實現的是NavigableMap接口。NavigableMap是SortedMap的一種,實現了對Map中key的排序。
這樣兩者的第一個區別就出來了,TreeMap是排序的而HashMap不是。
再看看HashMap和TreeMap的構造函數的區別。
public HashMap(int initialCapacity, float loadFactor)HashMap除了默認的無參構造函數之外,還可以接受兩個參數initialCapacity和loadFactor。
HashMap的底層結構是Node的數組:
transient Node<K,V>[] tableinitialCapacity就是這個table的初始容量。如果大家不傳initialCapacity,HashMap提供了一個默認的值:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16當HashMap中存儲的數據過多的時候,table數組就會被裝滿,這時候就需要擴容,HashMap的擴容是以2的倍數來進行的。而loadFactor就指定了什么時候需要進行擴容操作。默認的loadFactor是0.75。
static final float DEFAULT_LOAD_FACTOR = 0.75f;再來看幾個非常有趣的變量:
static final int TREEIFY_THRESHOLD = 8; static final int UNTREEIFY_THRESHOLD = 6; static final int MIN_TREEIFY_CAPACITY = 64;上面的三個變量有什么用呢?在java 8之前,HashMap解決hashcode沖突的方法是采用鏈表的形式,為了提升效率,java 8將其轉成了TreeNode。什么時候會發送這個轉換呢?
這時候就要看這兩個變量TREEIFY_THRESHOLD和UNTREEIFY_THRESHOLD。
有的同學可能發現了,TREEIFY_THRESHOLD為什么比UNTREEIFY_THRESHOLD大2呢?其實這個問題我也不知道,但是你看源代碼的話,用到UNTREEIFY_THRESHOLD時候,都用的是<=,而用到TREEIFY_THRESHOLD的時候,都用的是>= TREEIFY_THRESHOLD - 1,所以這兩個變量在本質上是一樣的。
MIN_TREEIFY_CAPACITY表示的是如果table轉換TreeNode的最小容量,只有capacity >= MIN_TREEIFY_CAPACITY的時候才允許TreeNode的轉換。
TreeMap和HashMap不同的是,TreeMap的底層是一個Entry:
private transient Entry<K,V> root他的實現是一個紅黑樹,方便用來遍歷和搜索。
TreeMap的構造函數可以傳入一個Comparator,實現自定義的比較方法。
public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}如果不提供自定義的比較方法,則使用的是key的natural order。
排序區別
我們講完兩者的本質之后,現在舉例說明,先看下兩者對排序的區別:
@Testpublic void withOrder(){Map<String, String> books = new HashMap<>();books.put("bob", "books");books.put("c", "concurrent");books.put("a", "a lock");log.info("{}",books);} @Testpublic void withOrder(){Map<String, String> books = new TreeMap<>();books.put("bob", "books");books.put("c", "concurrent");books.put("a", "a lock");log.info("{}",books);}同樣的代碼,一個使用了HashMap,一個使用了TreeMap,我們會發現TreeMap輸出的結果是排好序的,而HashMap的輸出結果是不定的。
Null值的區別
HashMap可以允許一個null key和多個null value。而TreeMap不允許null key,但是可以允許多個null value。
@Testpublic void withNull() {Map<String, String> hashmap = new HashMap<>();hashmap.put(null, null);log.info("{}",hashmap);} @Testpublic void withNull() {Map<String, String> hashmap = new TreeMap<>();hashmap.put(null, null);log.info("{}",hashmap);}HashMap會報出: NullPointerException。
性能區別
HashMap的底層是Array,所以HashMap在添加,查找,刪除等方法上面速度會非??臁6鳷reeMap的底層是一個Tree結構,所以速度會比較慢。
另外HashMap因為要保存一個Array,所以會造成空間的浪費,而TreeMap只保存要保持的節點,所以占用的空間比較小。
HashMap如果出現hash沖突的話,效率會變差,不過在java 8進行TreeNode轉換之后,效率有很大的提升。
TreeMap在添加和刪除節點的時候會進行重排序,會對性能有所影響。
共同點
兩者都不允許duplicate key,兩者都不是線程安全的。
本文的例子https://github.com/ddean2009/learn-java-collections
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
更多內容請訪問 www.flydean.com
總結
以上是生活随笔為你收集整理的深入理解HashMap和TreeMap的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Copy ArrayList的四种方式
- 下一篇: 深入理解HashMap和LinkedHa