關(guān)于HashSet與HashMap的區(qū)別及說明
HashSet與HashMap的區(qū)別
HashSet 集合不允許存儲(chǔ)相同的元素, 它底層實(shí)際上使用 HashMap 來存儲(chǔ)元素的,不過關(guān)注的只是key元素, 所有 value元素默認(rèn)為 Object類對(duì)象.
HashSet源碼如下
HashSet 的構(gòu)造方法
//HashSet底層用來存儲(chǔ)元素的結(jié)構(gòu),實(shí)際上使用HashMap來存儲(chǔ)
private transient HashMap<E,Object> map;
//HashMap中的value值,HashSet只關(guān)注key值,所以所有的value值都為Object對(duì)象
private static final Object PRESENT = new Object();
//HashSet的無參構(gòu)造,直接創(chuàng)建了一個(gè)HashMap對(duì)象
public HashSet() {
map = new HashMap<>();
}
//指定初始化容量和負(fù)載因子
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
//給定初始化容量
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}可以看到 HashSet的構(gòu)造方法底層都是調(diào)用 HashMap的構(gòu)造方法, 所以HashSet底層實(shí)際上是使用 HashMap 來作為存儲(chǔ)結(jié)構(gòu).
當(dāng)使用無參構(gòu)造創(chuàng)建 HashSet對(duì)象時(shí), 其實(shí)調(diào)用了 HashMap的無參構(gòu)造創(chuàng)建了一個(gè) HashMap對(duì)象, 所以 HashSet 的初始化容量也為16, 負(fù)載因子也為 0.75.
再來看看 HashSet 的 add() 方法的實(shí)現(xiàn):

可以看到 HashSet 的 add() 方法底層實(shí)際也是調(diào)用了 HashMap 的 put() 方法, 這里的key為我們傳入的將要添加到 set集合中的元素, 而value值則為 PERSENT,其實(shí)就是上面分析的 HashSet類中的一個(gè)靜態(tài)字段, 默認(rèn)為 Object對(duì)象.
HashSet并不關(guān)注value元素, 只使用 HashMap來存儲(chǔ) key元素, 這就使得 HashSet判斷元素相等的條件與 HashMap中 key相等的條件其實(shí)是一樣的, 兩個(gè)元素的 hashCode值相同且通過equals()方法比較返回 true.
所以HashSet應(yīng)該重寫 equals()和hashCode()方法, 兩個(gè)元素的 HashCode相同, 保證通過equals() 方法比較返回 true.
總結(jié)一下HashSet和HashMap的區(qū)別
(1)HashSet實(shí)現(xiàn)了Set接口, 僅存儲(chǔ)對(duì)象; HashMap實(shí)現(xiàn)了 Map接口, 存儲(chǔ)的是鍵值對(duì).
(2)HashSet底層其實(shí)是用HashMap實(shí)現(xiàn)存儲(chǔ)的, HashSet封裝了一系列HashMap的方法. 依靠HashMap來存儲(chǔ)元素值,(利用hashMap的key鍵進(jìn)行存儲(chǔ)), 而value值默認(rèn)為Object對(duì)象. 所以HashSet也不允許出現(xiàn)重復(fù)值, 判斷標(biāo)準(zhǔn)和HashMap判斷標(biāo)準(zhǔn)相同, 兩個(gè)元素的hashCode相等并且通過equals()方法返回true.
HashSet與HashMap的關(guān)系
HashSet作為一種最簡(jiǎn)單的java集合類,真的可以用三句話來概括一下:
第一句:存放不重復(fù)的數(shù)據(jù)。第二句:底層基于hash表實(shí)現(xiàn)。第三句:內(nèi)部基于HashMap。
這也就是說,你想要完完全全徹徹底底地把HashSet吃透,就一定要先吃透HashMap。這篇文章將帶著你從特點(diǎn)到存儲(chǔ),再到最后的實(shí)現(xiàn),從源碼角度來分析一下。
認(rèn)識(shí)
HashSet其實(shí)就是一個(gè)沒有重復(fù)數(shù)據(jù)的集合,基本用法很簡(jiǎn)單,我們直接給個(gè)例子。

以上只是列出了其最簡(jiǎn)單的用法。下面我們看看其繼承關(guān)系。
HashSet主要繼承了三個(gè)接口Serializable、Cloneable、Set,并且實(shí)現(xiàn)了抽象類AbstractSet。
我們直接看看源碼:

學(xué)過HashMap的人應(yīng)該都知道HashMap實(shí)現(xiàn)的是Map接口,而HashSet是Set接口。
下面我們就從源碼的角度來分析一下HashSet。
源碼分析
1、參數(shù)變量

這里有個(gè)問題,那就是既然HashSet只使用到了HashMap的key,為什么不使用null來充當(dāng)HashMap的value,而使用了PRESENT這個(gè)對(duì)象呢?
答:想要深入這個(gè)問題,我們還需要深入到源碼中看看:

以上兩個(gè)是增刪方法,在add一個(gè)元素的時(shí)候,其實(shí)調(diào)用的就是map.put(e, PRESENT)==null,HashMap在put元素的時(shí)候會(huì)出現(xiàn)兩種情況:
情況一:put的元素是新的,那么map.put會(huì)發(fā)現(xiàn)key沒有,那么直接插入即可。return結(jié)果為true。
情況二:put的元素是舊的,那么map.put會(huì)發(fā)現(xiàn)key已有,則直接返回相應(yīng)的value,也就是PRESENT,PRESENT不等于null,return的也就是false了,表示HashSet插入失敗。如果我們這里使用null為map.put的參數(shù)呢?直接返回相應(yīng)的value,也就是null,這時(shí)候null==null是true。竟然返回了true。很明顯就是錯(cuò)誤的返回結(jié)果呀。
這其實(shí)也是去重復(fù)的原理。對(duì)于刪除方法其實(shí)也是一樣的。
2、構(gòu)造函數(shù)

HashSet提供的構(gòu)造方法很多,有5個(gè),在這里我想說明的是每一種構(gòu)造方法,其實(shí)都是創(chuàng)建的HashMap。這也證明了我們文章開頭提到的內(nèi)部基于HashMap。
3、其他方法
增刪方法我們已經(jīng)提到了,在這里我們主要看一下其他方法。

上面的方法還包含了遍歷元素的方式。
HashSet就是這么簡(jiǎn)單,源碼里面幾乎所有的方法都是HashMap實(shí)現(xiàn)的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot Jar 包部署腳本的實(shí)例講解
在本篇文章里小編給大家整理的是一篇關(guān)于Spring Boot Jar 包部署腳本的實(shí)例講解內(nèi)容,對(duì)此有興趣的朋友們可以跟著學(xué)習(xí)下。2021-12-12
idea springboot遠(yuǎn)程debug的操作方法
這篇文章主要介紹了idea springboot遠(yuǎn)程debug的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
JAVA開發(fā)環(huán)境Vs?code配置步驟詳解
這篇文章主要為大家介紹了JAVA開發(fā)環(huán)境Vs?code配置步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
java解析xml匯總_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了java解析xml匯總_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理的相關(guān)資料,需要的朋友可以參考下2017-07-07
HashMap原理及手寫實(shí)現(xiàn)部分區(qū)塊鏈特征
這篇文章主要為大家介紹了HashMap原理及手寫實(shí)現(xiàn)部分區(qū)塊鏈特征,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Java StackTraceElement實(shí)例代碼
這篇文章主要介紹了Java StackTraceElement實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02

