Java 遍歷 HashSet 爲什麼輸出是有序的

1.問題

今天學習數據結構哈希表,知道了哈希表的犧牲了順序性,從而保證了效率。然後我想到了java中的 hashset和Treeset,我記得以前學習set的時候,老師說set元素是有序的,有就是說,hashset 和 treeset都是有序的。可是現在在學習數據結構的時候發現hashset 是無序的, treeset 是有序的,那這就和前面所說的set是有序的互相矛盾了,所以讓來驗證一下,如下:

 

使用的是 java 8 

HashSet<Integer> set = new HashSet<>();
set.add(3);
set.add(1);
set.add(2);
System.out.println("HashSet : " + set);

TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
System.out.println("TreeSet : " + treeSet);
HashSet : [1, 2, 3]
TreeSet : [1, 2, 3]

從結果得知,hashset和 treeset都是有序的輸出元素了。

這樣的結果其實和我前面所講的哈希表犧牲了有序這一命題所矛盾了,可是哈希表確實是犧牲了有序性,那麼問題在哪裏呢?

 

2.hashset實現原理

仔細回想,不難發現hashset底層使用hashmap來實現的,set的元素存放在map的key上面。

對於hashmap來講,它的主體是一個Entry數組,Entry又是一個鏈表,因此hashmap的存儲過程如下:

1.計算key的hash值

2.把得到的hash值作爲數據下標去存儲到Entry數組。在這裏可能出現不一樣的key的得到的hash值相等,如果相等,就把新的value存到當前下標下保存的Entry裏面(Entry是鏈表)

由此可見,hashset實際上存儲元素的時候進行了獲取hash的操作,而對於測試用例中的 3,2,1來說,他們的hash值就是自身,所以根據這個hash值得到數組的下標存儲元素後,表現出來就是有序的

 

3.treeset實現原理

treeset的底層使用treemap實現的,set的元素存放在map的key上面,

treemap又是用紅黑樹進行實現,實際上紅黑樹是一種自平衡二叉查找樹,它滿足元素的有序性,因此treeset的元素是具有有序性的。

 

4.結論

針對以上分析,在寫一次測試代碼

HashSet<Integer> set = new HashSet<>();
set.add(3);
set.add(1);
set.add(2);
System.out.println("HashSet : " + set);

TreeSet<Integer> treeSet = new TreeSet<>((m, n) -> n - m);
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
System.out.println("TreeSet : " + treeSet);
HashSet : [1, 2, 3]
TreeSet : [3, 2, 1]

 

這裏可以看出,Treeset按照我們實現的排序方法按照順序的打印出了結果。

這裏有人可能會問,你的hashset沒有添加和treeset相同的排序代碼,肯定結果不一樣啊;這是因爲hashset不支持添加比較器的構造方法,這也說明hashset所存儲的元素不需要具備可比較性

以上個人見解,歡迎評論區糾錯。

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