Java集合(九)--基于Map實(shí)現(xiàn)的Set簡(jiǎn)析

HashSet

此類由一個(gè)哈希表(實(shí)際上是一個(gè)HashMap實(shí)例)支持本缠。 不保證元素的順序,特別是逞度,它不保證順序隨著時(shí)間的推移保持不變悍手。該集合沒(méi)有重復(fù)的元素并且允許使用null元素。另外悠轩,跟之前分析的其他集合類一樣间狂,它是非同步的。

定義及說(shuō)明

定義如下:

public class HashSet<E> extends AbstractSet<E> 
       implements Set<E>, Cloneable, java.io.Serializable{}

1火架、繼承了AbstractSet并實(shí)現(xiàn)了Set接口鉴象,即擁有Set基本的方法和屬性

2、實(shí)現(xiàn)了Cloneable何鸡,即支持clone

3纺弊、實(shí)現(xiàn)了java.io.Serializable,即支持序列化和反序列化

構(gòu)造函數(shù)如下:

//底層map
private transient HashMap<E,Object> map;

//構(gòu)造一個(gè)新的空HashSet骡男,底層HashMap實(shí)例的具有默認(rèn)初始容量(16)和加載因子(0.75)
public HashSet() {
        map = new HashMap<>();
    }

//構(gòu)造一個(gè)包含指定集合中元素的新HashSet淆游。 使用默認(rèn)加載因子(0.75)創(chuàng)建HashMap,初始容量要求足以包含指定集合中的元素
public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

//構(gòu)造一個(gè)空的新HashSet隔盛,底層HashMap具有指定的初始容量和加載因子
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

//構(gòu)造一個(gè)新的空HashSet犹菱,底層HashMap實(shí)例具有指定的初始容量和默認(rèn)加載因子(0.75)
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

//構(gòu)造一個(gè)新的空HashSet。(此包私有構(gòu)造函數(shù)僅由LinkedHashSet使用)吮炕。底層HashMap實(shí)例是具有指定初始容量和指定加載因子的LinkedHashMap腊脱。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

我們發(fā)現(xiàn),其實(shí)實(shí)例化一個(gè)HashSet的本質(zhì)就是實(shí)例化一個(gè)HashMap龙亲,所以說(shuō)陕凹,HashSet是由HashMap支持的。這里第二個(gè)構(gòu)造函數(shù)中鳄炉,有一個(gè)計(jì)算"Math.max((int) (c.size()/.75f) + 1, 16)"杜耙,這是因?yàn)镠ashMap的大小必須是2的指數(shù)倍,通過(guò)"c.size()/.75f"計(jì)算出來(lái)的實(shí)際大小跟16相比迎膜,如果小于16泥技,就直接使用16作為初始值,避免一些重復(fù)的計(jì)算磕仅。

源碼簡(jiǎn)析

我們看一下它的一些基本方法:

//與底層Map中的Object關(guān)聯(lián)的虛擬值
private static final Object PRESENT = new Object();

//返回此set中元素的迭代器
public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

//獲取大小
public int size() {
        return map.size();
    }

//判空
public boolean isEmpty() {
        return map.isEmpty();
    }

//判斷是否包含指定元素
public boolean contains(Object o) {
        return map.containsKey(o);
    }

//添加元素
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

//刪除元素
public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

//清空集合
public void clear() {
        map.clear();
    }

我們可以看到珊豹,他所有的基本方法都是通過(guò)調(diào)用HashMap對(duì)應(yīng)的方法來(lái)實(shí)現(xiàn)的,它的元素就是底層HashMap的key榕订,而底層HashMap的值都是一個(gè)Object類型的PRESENT對(duì)象店茶。

LinkedHashSet

Set接口的哈希表和鏈表實(shí)現(xiàn),具有可預(yù)測(cè)的迭代順序劫恒。此實(shí)現(xiàn)與HashSet的不同之處在于它維護(hù)了一個(gè)貫穿其所有條目的雙向鏈表贩幻。此鏈接列表定義迭代排序轿腺,即元素插入集合(插入順序)的順序。請(qǐng)注意丛楚,如果將元素重新插入到集合中族壳,則不會(huì)影響插入順序。(如果s.contains(e)在調(diào)用之前立即返回true趣些,則在調(diào)用s.add(e)時(shí)仿荆,將元素e重新插入到集合中)。該集合允許使用null元素坏平,且該集合是非同步的拢操。

定義及說(shuō)明

定義如下:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {}

1、它是繼承了HashSet并實(shí)現(xiàn)了Set接口舶替,所以具有Set基本的方法和屬性

2令境、實(shí)現(xiàn)了Cloneable,即支持clone

3顾瞪、實(shí)現(xiàn)了java.io.Serializable舔庶,即支持序列化和反序列化

構(gòu)造方法有:

//使用指定的初始容量和加載因子構(gòu)造一個(gè)新的空LinkedHashSet
public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

//使用指定的初始容量和默認(rèn)加載因子(0.75)構(gòu)造一個(gè)新的空鏈?zhǔn)焦<疞inkedHashSet
public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

//使用默認(rèn)初始容量(16)和加載因子(0.75)構(gòu)造一個(gè)新的空LinkedHashSet
public LinkedHashSet() {
        super(16, .75f, true);
    }

//構(gòu)造一個(gè)新的LinkedHashSet,其具有與指定集合相同的元素玲昧。 創(chuàng)建鏈接的哈希集的初始容量足以容納指定集合中的元素栖茉,具有默認(rèn)加載因子(0.75)
public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }

我們可以發(fā)現(xiàn)篮绿,它其實(shí)就是通過(guò)調(diào)用HashSet的構(gòu)造方法來(lái)進(jìn)行實(shí)例化孵延。它和HashSet的區(qū)別就在于LinkedHashSet的元素是按照放入順序進(jìn)行排列。并且LinkedHashSet內(nèi)部使用LinkedHashMap實(shí)現(xiàn)亲配。

其他方法:

@Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }

它只重寫了HashSet的spliterator()方法(這個(gè)方法后期分析)尘应。

TreeSet

基于TreeMap的NavigableSet實(shí)現(xiàn)。元素使用其可比較的自然順序或在創(chuàng)建時(shí)創(chuàng)建時(shí)提供的Comparator進(jìn)行排序吼虎,具體取決于使用的構(gòu)造函數(shù)犬钢。它的基本操作(add、remove思灰、和contains)的時(shí)間復(fù)雜度為log(n)玷犹。當(dāng)然,它也是非同步的洒疚,不過(guò)不支持null元素歹颓。

定義及說(shuō)明

定義如下:

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable{}

1、繼承與AbstractSet油湖,實(shí)現(xiàn)了NavigableSet接口巍扛,即擁有Set的基本的方法和屬性。其元素使用其可比較的自然順序乏德,或者通過(guò)在創(chuàng)建時(shí)提供的Comparator進(jìn)行排序撤奸。

2吠昭、實(shí)現(xiàn)了Cloneable,即支持clone

3胧瓜、實(shí)現(xiàn)了java.io.Serializable矢棚,即支持序列化和反序列化

構(gòu)造函數(shù):

//底層映射
private transient NavigableMap<E,Object> m;

//構(gòu)造由指定的可導(dǎo)航映射支持的TreeSet
 TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

//構(gòu)造一個(gè)新的空TreeSet,根據(jù)其元素的自然順序進(jìn)行排序府喳。 插入集合中的所有元素都必須實(shí)現(xiàn)Comparable接口幻妓。 此外,所有這些元素必須是可相互比較的
public TreeSet() {
        this(new TreeMap<E,Object>());
    }

//構(gòu)造一個(gè)新的空TreeSet劫拢,根據(jù)指定的比較器進(jìn)行排序肉津。 插入到集合中的所有元素必須通過(guò)指定的比較器相互比較
public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

//構(gòu)造一個(gè)新的TreeSet,其中包含指定集合中的元素舱沧,并根據(jù)其元素的自然順序進(jìn)行排序妹沙。 插入集合中的所有元素都必須實(shí)現(xiàn)Comparable接口
public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }

//構(gòu)造一個(gè)包含相同元素并使用與指定有序集相同排序的新TreeSet
public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }

可以看出,TreeSet是基于TreeMap實(shí)現(xiàn)的熟吏。在addAll()方法中距糖,其實(shí)也是調(diào)用了TreeMap進(jìn)行元素的添加。

源碼簡(jiǎn)析

再看一下一些基本的方法:

private static final Object PRESENT = new Object();

public boolean isEmpty() {
        return m.isEmpty();
    }

public boolean contains(Object o) {
        return m.containsKey(o);
    }


public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }

public void clear() {
        m.clear();
    }

它也是通過(guò)調(diào)用Map的相關(guān)的方法牵寺,來(lái)實(shí)現(xiàn)功能悍引。它的元素就是底層Map的key,而底層Map的值都是一個(gè)Object類型的PRESENT對(duì)象帽氓。

小結(jié)

好了趣斤,到這就分析完了,總結(jié)一下:

Set集合中黎休,不允許有重復(fù)的元素浓领。且Set對(duì)兩個(gè)元素的比較不是使用"=="而是使用"equals"。HashSet和TreeSet是根據(jù)元素的自然順序或者構(gòu)建時(shí)傳入的Comparator比較器對(duì)元素進(jìn)行排序势腮,而LinkedHashSet是根據(jù)元素的插入順序進(jìn)行排序联贩。HashSet和LinkedHashSet允許有null元素,但是TreeSet不允許捎拯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泪幌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子署照,更是在濱河造成了極大的恐慌祸泪,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藤树,死亡現(xiàn)場(chǎng)離奇詭異浴滴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)岁钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門升略,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)微王,“玉大人,你說(shuō)我怎么就攤上這事品嚣】惶龋” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵翰撑,是天一觀的道長(zhǎng)罩旋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)眶诈,這世上最難降的妖魔是什么涨醋? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮逝撬,結(jié)果婚禮上浴骂,老公的妹妹穿的比我還像新娘。我一直安慰自己宪潮,他們只是感情好溯警,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狡相,像睡著了一般梯轻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尽棕,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天喳挑,我揣著相機(jī)與錄音,去河邊找鬼萄金。 笑死蟀悦,一個(gè)胖子當(dāng)著我的面吹牛媚朦,可吹牛的內(nèi)容都是我干的氧敢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼询张,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼孙乖!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起份氧,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤唯袄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜗帜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恋拷,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年厅缺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔬顾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宴偿。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诀豁,靈堂內(nèi)的尸體忽然破棺而出窄刘,到底是詐尸還是另有隱情,我是刑警寧澤舷胜,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布娩践,位于F島的核電站,受9級(jí)特大地震影響烹骨,放射性物質(zhì)發(fā)生泄漏翻伺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一沮焕、第九天 我趴在偏房一處隱蔽的房頂上張望穆趴。 院中可真熱鬧,春花似錦遇汞、人聲如沸未妹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)络它。三九已至,卻和暖如春歪赢,著一層夾襖步出監(jiān)牢的瞬間化戳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工埋凯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留点楼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓白对,卻偏偏與公主長(zhǎng)得像掠廓,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甩恼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349