死磕 java集合之ConcurrentSkipListSet源碼分析——Set大彙總

来源:https://www.cnblogs.com/tong-yuan/archive/2019/04/18/ConcurrentSkipListSet.html
-Advertisement-
Play Games

ConcurrentSkipListSet的底層是ConcurrentSkipListMap嗎? ConcurrentSkipListSet是線程安全的嗎? ConcurrentSkipListSet是有序的嗎? ConcurrentSkipListSet和之前講的Set有何不同? ...


問題

(1)ConcurrentSkipListSet的底層是ConcurrentSkipListMap嗎?

(2)ConcurrentSkipListSet是線程安全的嗎?

(3)ConcurrentSkipListSet是有序的嗎?

(4)ConcurrentSkipListSet和之前講的Set有何不同?

簡介

ConcurrentSkipListSet底層是通過ConcurrentNavigableMap來實現的,它是一個有序的線程安全的集合。

源碼分析

它的源碼比較簡單,跟通過Map實現的Set基本是一致,只是多了一些取最近的元素的方法。

為了保持專欄的完整性,我還是貼一下源碼,最後會對Set的整個家族作一個對比,有興趣的可以直接拉到最下麵。

// 實現了NavigableSet介面,並沒有所謂的ConcurrentNavigableSet介面
public class ConcurrentSkipListSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable {

    private static final long serialVersionUID = -2479143111061671589L;

    // 存儲使用的map
    private final ConcurrentNavigableMap<E,Object> m;

    // 初始化
    public ConcurrentSkipListSet() {
        m = new ConcurrentSkipListMap<E,Object>();
    }

    // 傳入比較器
    public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }
    
    // 使用ConcurrentSkipListMap初始化map
    // 並將集合c中所有元素放入到map中
    public ConcurrentSkipListSet(Collection<? extends E> c) {
        m = new ConcurrentSkipListMap<E,Object>();
        addAll(c);
    }
    
    // 使用ConcurrentSkipListMap初始化map
    // 並將有序Set中所有元素放入到map中
    public ConcurrentSkipListSet(SortedSet<E> s) {
        m = new ConcurrentSkipListMap<E,Object>(s.comparator());
        addAll(s);
    }
    
    // ConcurrentSkipListSet類內部返回子set時使用的
    ConcurrentSkipListSet(ConcurrentNavigableMap<E,Object> m) {
        this.m = m;
    }
    
    // 克隆方法
    public ConcurrentSkipListSet<E> clone() {
        try {
            @SuppressWarnings("unchecked")
            ConcurrentSkipListSet<E> clone =
                (ConcurrentSkipListSet<E>) super.clone();
            clone.setMap(new ConcurrentSkipListMap<E,Object>(m));
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }

    /* ---------------- Set operations -------------- */
    // 返回元素個數
    public int size() {
        return m.size();
    }

    // 檢查是否為空
    public boolean isEmpty() {
        return m.isEmpty();
    }
    
    // 檢查是否包含某個元素
    public boolean contains(Object o) {
        return m.containsKey(o);
    }
    
    // 添加一個元素
    // 調用map的putIfAbsent()方法
    public boolean add(E e) {
        return m.putIfAbsent(e, Boolean.TRUE) == null;
    }
    
    // 移除一個元素
    public boolean remove(Object o) {
        return m.remove(o, Boolean.TRUE);
    }

    // 清空所有元素
    public void clear() {
        m.clear();
    }
    
    // 迭代器
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }

    // 降序迭代器
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }


    /* ---------------- AbstractSet Overrides -------------- */
    // 比較相等方法
    public boolean equals(Object o) {
        // Override AbstractSet version to avoid calling size()
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        try {
            // 這裡是通過兩次兩層for迴圈來比較
            // 這裡是有很大優化空間的,參考上篇文章CopyOnWriteArraySet中的彩蛋
            return containsAll(c) && c.containsAll(this);
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }
    
    // 移除集合c中所有元素
    public boolean removeAll(Collection<?> c) {
        // Override AbstractSet version to avoid unnecessary call to size()
        boolean modified = false;
        for (Object e : c)
            if (remove(e))
                modified = true;
        return modified;
    }

    /* ---------------- Relational operations -------------- */
    
    // 小於e的最大元素
    public E lower(E e) {
        return m.lowerKey(e);
    }

    // 小於等於e的最大元素
    public E floor(E e) {
        return m.floorKey(e);
    }
    
    // 大於等於e的最小元素
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }

    // 大於e的最小元素
    public E higher(E e) {
        return m.higherKey(e);
    }

    // 彈出最小的元素
    public E pollFirst() {
        Map.Entry<E,Object> e = m.pollFirstEntry();
        return (e == null) ? null : e.getKey();
    }

    // 彈出最大的元素
    public E pollLast() {
        Map.Entry<E,Object> e = m.pollLastEntry();
        return (e == null) ? null : e.getKey();
    }


    /* ---------------- SortedSet operations -------------- */

    // 取比較器
    public Comparator<? super E> comparator() {
        return m.comparator();
    }

    // 最小的元素
    public E first() {
        return m.firstKey();
    }

    // 最大的元素
    public E last() {
        return m.lastKey();
    }
    
    // 取兩個元素之間的子set
    public NavigableSet<E> subSet(E fromElement,
                                  boolean fromInclusive,
                                  E toElement,
                                  boolean toInclusive) {
        return new ConcurrentSkipListSet<E>
            (m.subMap(fromElement, fromInclusive,
                      toElement,   toInclusive));
    }
    
    // 取頭子set
    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
    }

    // 取尾子set
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
    }

    // 取子set,包含from,不包含to
    public NavigableSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }
    
    // 取頭子set,不包含to
    public NavigableSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }
    
    // 取尾子set,包含from
    public NavigableSet<E> tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }
    
    // 降序set
    public NavigableSet<E> descendingSet() {
        return new ConcurrentSkipListSet<E>(m.descendingMap());
    }

    // 可分割的迭代器
    @SuppressWarnings("unchecked")
    public Spliterator<E> spliterator() {
        if (m instanceof ConcurrentSkipListMap)
            return ((ConcurrentSkipListMap<E,?>)m).keySpliterator();
        else
            return (Spliterator<E>)((ConcurrentSkipListMap.SubMap<E,?>)m).keyIterator();
    }

    // 原子更新map,給clone方法使用
    private void setMap(ConcurrentNavigableMap<E,Object> map) {
        UNSAFE.putObjectVolatile(this, mapOffset, map);
    }

    // 原子操作相關內容
    private static final sun.misc.Unsafe UNSAFE;
    private static final long mapOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = ConcurrentSkipListSet.class;
            mapOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("m"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

可以看到,ConcurrentSkipListSet基本上都是使用ConcurrentSkipListMap實現的,雖然取子set部分是使用ConcurrentSkipListMap中的內部類,但是這些內部類其實也是和ConcurrentSkipListMap相關的,它們返回ConcurrentSkipListMap的一部分數據。

另外,這裡的equals()方法實現的相當敷衍,有很大的優化空間,作者這樣實現,應該也是知道幾乎沒有人來調用equals()方法吧。

總結

(1)ConcurrentSkipListSet底層是使用ConcurrentNavigableMap實現的;

(2)ConcurrentSkipListSet有序的,基於元素的自然排序或者通過比較器確定的順序;

(3)ConcurrentSkipListSet是線程安全的;

彩蛋

Set大彙總:

Set 有序性 線程安全 底層實現 關鍵介面 特點
HashSet HashMap 簡單
LinkedHashSet LinkedHashMap 插入順序
TreeSet NavigableMap NavigableSet 自然順序
CopyOnWriteArraySet CopyOnWriteArrayList 插入順序,讀寫分離
ConcurrentSkipListSet ConcurrentNavigableMap NavigableSet 自然順序

從中我們可以發現一些規律:

(1)除了HashSet其它Set都是有序的;

(2)實現了NavigableSet或者SortedSet介面的都是自然順序的;

(3)使用併發安全的集合實現的Set也是併發安全的;

(4)TreeSet雖然不是全部都是使用的TreeMap實現的,但其實都是跟TreeMap相關的(TreeMap的子Map中組合了TreeMap);

(5)ConcurrentSkipListSet雖然不是全部都是使用的ConcurrentSkipListMap實現的,但其實都是跟ConcurrentSkipListMap相關的(ConcurrentSkipListeMap的子Map中組合了ConcurrentSkipListMap);


歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

qrcode


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上個月試了一波水,記錄了面試官的問題,問題答案自己對應的百度,希望對大家有幫助 1.設計模式的六大原則2.如果讓你設計一個RPC,你怎麼設計3.高併發的單列模式4.連接池的submit和execute的聯繫和區別5.怎麼避免重覆消費6.講一講noi和bio7.既然講到了selector 那你講講se ...
  • php與mysql資料庫,PHP支持很多資料庫,與mysql為牛逼組合,mysql資料庫的基礎知識的掌握是由必要的,要瞭解如何操作mysql資料庫,數據表的方法。 什麼是資料庫,資料庫能做什麼,資料庫有什麼好處,資料庫的基礎必備技術,備份和恢復的方法。 mysql的好處,功能強大,支持跨平臺,運行速 ...
  • 進行文件上傳前需要添加相應的依賴 在xml文件中進行相應的文件上傳解析器的配置 註意:這裡有個坑,因為沒註意,再排查錯誤的時候花了一點時間。就是給bean的id一定要是。 否者就會報如下的錯誤: ...
  • 1,今日內容: 二,深淺拷貝 三,元組類型 四,字典類型 五,字典的定義 六,字典的操作 七,集合類型 ...
  • 關於SpringMVC頁面向Controller傳參的問題,看了網上不少帖子,大多總結為以下幾類: 1、直接把頁面表單中相關元素的name屬性對應的值作為Controller方法中的形參。 這個應該是最直接的,我看的那本書從百度的編輯器中取內容content時就直接用的這個方法: 2、通過@Requ ...
  • 一、什麼是進程 進程(Process)是電腦中的程式關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。在早期面向進程設計的電腦結構中,進程是程式的基本執行實體;在當代面向線程設計的電腦結構中,進程是線程的容器。程式是指令、數據及其組織形式的描述,進程是程 ...
  • In many programming competitions, we are asked to find (or count the number of) Prime Factors of an integer i. This is boring. This time, let’s count ...
  • 字元串是一種非常重要的數據類型,但是C語言不存在顯式的字元串類型,C語言中的字元串都以字元串常量的形式出現或存儲在字元數組中。同時,C 語言提供了一系列庫函數來對操作字元串,這些庫函數都包含在頭文件 string.h 中。 一、字元串常量和字元數組 1.1、什麼是字元串常量 C 語言雖然沒有字元串類 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...