Set——你真的了解嗎?

JAVA 基礎(chǔ) :Set——你真的了解嗎污秆?

簡述

Set 繼承于 Collection 劈猪,是一種集合。有元素無序良拼、值不重復战得、不允許空值得特性。主要有HashSet庸推、TreeSet兩種實現(xiàn)方式常侦。由于Set主要基于Map實現(xiàn),所以特點也由Map決定予弧。

Set 結(jié)構(gòu)圖

例如 HashSet ,調(diào)用 HashSet 的無參構(gòu)造函數(shù)刮吧,HashSet 會使用默認的 HashMap ,初始化 Size 為16掖蛤,擴張系數(shù)為0.75

HashSet

官方文檔
官方文檔翻譯
構(gòu)造方法官方文檔
構(gòu)造方法官方文檔翻譯
HashSet 結(jié)構(gòu)圖

查看 HashSet 源碼會發(fā)現(xiàn)主要數(shù)據(jù)操作都間接調(diào)用 HashMap 的數(shù)據(jù)操作杀捻,從 add() 方法可以看出 HashSet 的值其實為 HashMap 的 Key,而 Value 是一個關(guān)鍵字為 final 類型為 Object 的 PRESENT 蚓庭,遍歷的 HashSet 的值其實是遍歷 HashMap 的 KeyEntry .

HashSet 源碼

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * Constructs a new set containing the elements in the specified
     * collection.  The <tt>HashMap</tt> is created with default load factor
     * (0.75) and an initial capacity sufficient to contain the elements in
     * the specified collection.
     *
     * @param c the collection whose elements are to be placed into this set
     * @throws NullPointerException if the specified collection is null
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * the specified initial capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * the specified initial capacity and default load factor (0.75).
     *
     * @param      initialCapacity   the initial capacity of the hash table
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * Constructs a new, empty linked hash set.  (This package private
     * constructor is only used by LinkedHashSet.) The backing
     * HashMap instance is a LinkedHashMap with the specified initial
     * capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @param      dummy             ignored (distinguishes this
     *             constructor from other int, float constructor.)
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    /**
     * Returns an iterator over the elements in this set.  The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set
     * @see ConcurrentModificationException
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    /**
     * Returns the number of elements in this set (its cardinality).
     *
     * @return the number of elements in this set (its cardinality)
     */
    public int size() {
        return map.size();
    }

    /**
     * Returns <tt>true</tt> if this set contains no elements.
     *
     * @return <tt>true</tt> if this set contains no elements
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * Returns <tt>true</tt> if this set contains the specified element.
     * More formally, returns <tt>true</tt> if and only if this set
     * contains an element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this set is to be tested
     * @return <tt>true</tt> if this set contains the specified element
     */
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    /**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     * Removes the specified element from this set if it is present.
     * More formally, removes an element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>,
     * if this set contains such an element.  Returns <tt>true</tt> if
     * this set contained the element (or equivalently, if this set
     * changed as a result of the call).  (This set will not contain the
     * element once the call returns.)
     *
     * @param o object to be removed from this set, if present
     * @return <tt>true</tt> if the set contained the specified element
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * Removes all of the elements from this set.
     * The set will be empty after this call returns.
     */
    public void clear() {
        map.clear();
    }

    /**
     * Returns a shallow copy of this <tt>HashSet</tt> instance: the elements
     * themselves are not cloned.
     *
     * @return a shallow copy of this set
     */
    @SuppressWarnings("unchecked")
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    /**
     * Save the state of this <tt>HashSet</tt> instance to a stream (that is,
     * serialize it).
     *
     * @serialData The capacity of the backing <tt>HashMap</tt> instance
     *             (int), and its load factor (float) are emitted, followed by
     *             the size of the set (the number of elements it contains)
     *             (int), followed by all of its elements (each an Object) in
     *             no particular order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }

    /**
     * Reconstitute the <tt>HashSet</tt> instance from a stream (that is,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }

        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum capacity.
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Create backing HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }

    /**
     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
     * and <em>fail-fast</em> {@link Spliterator} over the elements in this
     * set.
     *
     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
     * {@link Spliterator#DISTINCT}.  Overriding implementations should document
     * the reporting of additional characteristic values.
     *
     * @return a {@code Spliterator} over the elements in this set
     * @since 1.8
     */
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }
}

TreeSet

TreeSet 和 HashSet 實現(xiàn)類似致讥,間接調(diào)用內(nèi)部的 TreeMap ,都是利用紅黑樹算法實現(xiàn)器赞;TreeSet 會根據(jù)其元素的自然順序?qū)υ剡M行排序,元素依然是唯一的不可重復垢袱,元素不可為 null .

TreeSet 結(jié)構(gòu)圖

LinkedHashSet

介于 HashSet 與 TreeSet 之間,在 HashSet 的基礎(chǔ)上增加了一個記錄插入順序的雙鏈表港柜。線程不安全有序不重復集合请契,基于 LinkedHashMap 實現(xiàn)咳榜,是 HashMap 與雙向鏈表結(jié)合實現(xiàn)的,利用雙向鏈表記錄插入順序爽锥,以保證迭代輸出的有序性涌韩。

LinkedHashSet 結(jié)構(gòu)圖

ConcurrentSkipListSet

線程安全的有序不重復集合,適用于高并發(fā)場景氯夷;與 TreeSet 對比臣樱,相同點是都是有序集合,不同點有兩方面腮考,第一 TreeSet 是非線程安全的雇毫,第二 ConcurrentSkipListSet 是基于 ConcurrentSkipListMap 通過跳表數(shù)據(jù)結(jié)構(gòu)實現(xiàn)而 TreeSet 是基于 TreeMap 通過紅黑樹算法實現(xiàn)。

ConcurrentSkipListSet 結(jié)構(gòu)圖

CopyOnWriteArraySet

線程安全的無序不重復集合踩蔚,適用于高并發(fā)場景棚放;與 HashSet 對比,相同點是都是無序集合寂纪,不同點有有兩個席吴,第一 HashSet 是非線程安全的,第二 CopyOnWriteArraySet 是基于 CopyOnWriteArrayList 通過動態(tài)數(shù)組數(shù)據(jù)結(jié)構(gòu)實現(xiàn)而 HashSet 是基于 HashMap 通過散列表數(shù)據(jù)結(jié)構(gòu)實現(xiàn)捞蛋。

CopyOnWriteArraySet 結(jié)構(gòu)圖

EnumSet

Set針對枚舉類型的接口實現(xiàn)類;通過位向量實現(xiàn)柬姚;EnumSet 中所有元素都必須是指定的枚舉類型或枚舉值拟杉,由 EnumSet 創(chuàng)建時指定,集合元素為有序量承、不重復搬设、非 null ,元素的順序與枚舉類元素順序相同撕捍;

EnumSet 結(jié)構(gòu)圖

JobStateReasons

JobStateReasons 結(jié)構(gòu)圖

ConcurrentHashMap.KeySetView

KeySetView 結(jié)構(gòu)圖

如有寫的不對的地方請大家指正拿穴,萬分感謝,相互學習忧风,相互交流

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末默色,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子狮腿,更是在濱河造成了極大的恐慌腿宰,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缘厢,死亡現(xiàn)場離奇詭異吃度,居然都是意外死亡,警方通過查閱死者的電腦和手機贴硫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門椿每,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事间护∫嗌” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵兑牡,是天一觀的道長央碟。 經(jīng)常有香客問我,道長均函,這世上最難降的妖魔是什么亿虽? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮苞也,結(jié)果婚禮上洛勉,老公的妹妹穿的比我還像新娘。我一直安慰自己如迟,他們只是感情好收毫,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著殷勘,像睡著了一般此再。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玲销,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天输拇,我揣著相機與錄音,去河邊找鬼贤斜。 笑死策吠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瘩绒。 我是一名探鬼主播猴抹,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锁荔!你這毒婦竟也來了蟀给?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤堕战,失蹤者是張志新(化名)和其女友劉穎坤溃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘱丢,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡薪介,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了越驻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汁政。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡道偷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出记劈,到底是詐尸還是另有隱情勺鸦,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布目木,位于F島的核電站换途,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刽射。R本人自食惡果不足惜军拟,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望誓禁。 院中可真熱鬧懈息,春花似錦、人聲如沸摹恰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俗慈。三九已至姑宽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闺阱,已是汗流浹背低千。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馏颂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓棋傍,卻偏偏與公主長得像救拉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瘫拣,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Java集合類可用于存儲數(shù)量不等的對象,并可以實現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)如棧,隊列等,Java集合還可以用于保存具有映射關(guān)...
    小徐andorid閱讀 1,942評論 0 13
  • 上一篇文章介紹了Set集合的通用知識亿絮。Set集合中包含了三個比較重要的實現(xiàn)類:HashSet、TreeSet和En...
    Ruheng閱讀 15,648評論 3 57
  • 操作系統(tǒng)中 heap 和 stack 的區(qū)別 什么是基于注解的切面實現(xiàn) 什么是 對象/關(guān)系 映射集成模塊 什么是 ...
    城市里永遠的學習者閱讀 754評論 0 49
  • 一淮椰、Java集合框架概述 集合可以看作是一種容器五慈,用來存儲對象信息纳寂。所有集合類都位于java.util包下,但支持...
    風平浪靜如碼閱讀 567評論 0 1
  • 集合概述 集合用來儲存數(shù)量不等的對象泻拦,且只能保存對象毙芜,實際保存的是對象的引用變量 主要由兩個接口派生而出,Coll...
    Utte閱讀 380評論 0 0