Java TreeMultiSet-支持可重復(fù)元素的TreeSet

TreeMultiSet

基于TreeMap實現(xiàn)的支持可重復(fù)元素的TreeSet
github地址藻治,歡迎star

為什么要開發(fā)這個數(shù)據(jù)結(jié)構(gòu)

搞過java的人應(yīng)該都知道TreeSet,但是TreeSet是不支持重復(fù)元素的帝嗡。有人會說,那用ArrayList或LinkedList不就可以了嗎神妹?
確實雨饺,ArrayList或LinkedList天然不去重荐吉,可以滿足支持重復(fù)元素的需求。但是蝗拿,我不僅需要支持可重復(fù)元素晾捏,而且需要數(shù)據(jù)實時保持有序。
這里有人又會說了哀托,有序不很簡單么惦辛?我把數(shù)據(jù)插入到ArrayList或LinkedList中之后,調(diào)用sort不就完事了嗎仓手?
嗯胖齐,確實,如果你的列表不經(jīng)常發(fā)生變化(sort的次數(shù)非常非常少)嗽冒,那么你使用List+sort當(dāng)然沒問題咯呀伙。但是,如果數(shù)據(jù)是不斷發(fā)生變化的怎么辦呢添坊?
如果數(shù)據(jù)不斷發(fā)生變化剿另,而且又需要保證數(shù)據(jù)實時有序(比如有實時查詢最小和最大的需求),如果你使用List+sort的方式這樣的時間復(fù)雜度非常高:
大概是插入排序的思路,這樣的單次插入時間復(fù)雜度是O(n)雨女,如果有n個元素柬甥,則需要O(n2)嚼松。因此呢,如果我們要降低時間復(fù)雜度,就不能使用List滓技。
然后返劲,接下來又有人說了病苗,我直接用TreeMap似乎可以啊惩嘉。TreeMap的key是元素,value是key對應(yīng)元素出現(xiàn)的次數(shù)乱灵。這樣就可以滿足插入可重復(fù)且有序的需求了塑崖。
的的確確,這個確實可以大致滿足可重復(fù)且有序的需求痛倚。但是规婆,我這里舉幾個使用TreeMap來滿足可重復(fù)且有序需求的缺點:

  1. 如果我們需要知道可重復(fù)元素集合的個數(shù)(重復(fù)元素算多個),使用TreeMap不能立馬且在O(1)時間復(fù)雜度之內(nèi)獲取到蝉稳,
    而需要for循環(huán)所有key抒蚜,然后計算所有value值之和。大致代碼如下:
    int size = 0;
    for (Integer num : treeMap.keySet()) {
        size += treeMap.get(num);
    }

這樣看起來是不是有點麻煩耘戚?獲取集合個數(shù)我們期望的應(yīng)該是下面這樣這樣簡潔(TreeMultiSet就能達(dá)到嗡髓,下面會介紹):

    set.size();
  1. 如果我們需要遍歷集合中的所有元素,重復(fù)元素需要遍歷多次(類似list的遍歷)收津。那么使用TreeMap來實現(xiàn)需要使用二重循環(huán)饿这,不太直觀。如下所示:
    for (Integer num : treeMap.keySet()) {
        int count = treeMap.get(num);
        while ((count--) > 0) {
            // 需要使用循環(huán)將num輸出count次
            System.out.println(num);
        }
    }

同樣撞秋,我們期望的應(yīng)該是一重循環(huán)搞定长捧,像下面這樣:

    for (Integer num : set) {
        System.out.println(num);
    }
  1. 如果我們需要刪除集合中指定個數(shù)的某個元素。舉個例子吻贿,集合[1, 2, 2, 3, 3串结,3]。我只想刪除兩個3舅列,TreeMap需要這么做:
    if (treeMap.containsKey(3)) {
        treeMap.put(2, treeMap.get(3) - 2);
    }

同樣奉芦,我們期望的應(yīng)該是像下面這樣:

    set.remove(3, 2); // 第二個參數(shù)代表需要刪除的元素的個數(shù)
  1. 如果我們需要往集合中添加指定數(shù)量的元素。舉個例子剧蹂,集合[1, 2, 2, 2, 4],我們需要添加兩個4烦却,TreeMap需要這么做:
    treeMap.put(4, treeMap.getOrDefault(4, 0) + 2);

同樣宠叼,我們期望的應(yīng)該像下面這樣:

    set.add(4, 2); // 第二個參數(shù)代表需要插入的元素的個數(shù)

除此之外,TreeMap來實現(xiàn)可重復(fù)treeSet的功能還有一些不太優(yōu)雅的地方,這里就不一一列舉了冒冬。

因此伸蚯,基于以上原因,TreeMultiSet誕生了简烤。(不過聲明一下:不是重復(fù)造輪子剂邮,而是站在巨人的肩膀上,TreeMultiSet大部分是參考TreeSet來實現(xiàn)的横侦,
其實都是基于TreeMap挥萌,而TreeMap底層是通過紅黑樹(Red-Black-Tree)來實現(xiàn)的,這也正是TreeSet的remove操作可達(dá)到O(logN)時間復(fù)雜度的本質(zhì)原因枉侧,有興趣的同學(xué)可以去研究一下

如何使用

gradle

Step 1. Add the JitPack repository in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2. Add the dependency in your app build.gradle:

dependencies {
    implementation 'com.github.yuruiyin:TreeMultiSet:1.0.1'
}

功能列表

功能描述 對應(yīng)方法
無參構(gòu)造函數(shù) TreeMultiSet()
帶比較器參數(shù)的構(gòu)造函數(shù) TreeMultiSet(Comparator<? super E> comparator)
帶集合參數(shù)構(gòu)造函數(shù) TreeMultiSet(Collection<? extends E> c)
帶SortedSet參數(shù)構(gòu)造函數(shù) TreeMultiSet(SortedSet<E> s)
返回所有元素(重復(fù)元素要next多次)的正向迭代器 Iterator<E> iterator()
返回所有元素(重復(fù)元素要next多次)的反向迭代器 Iterator<E> descendingIterator()
返回所有不相同元素的正向迭代器 Iterator<E> diffIterator()
返回所有不相同元素的反向迭代器 Iterator<E> diffDescendingIterator()
返回逆序Set NavigableSet<E> descendingSet()
返回指定頭尾元素的連續(xù)子集 NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
返回頭部連續(xù)子集 NavigableSet<E> headSet(E toElement, boolean inclusive)
返回尾部連續(xù)子集 NavigableSet<E> tailSet(E fromElement, boolean inclusive)
返回比較器 Comparator<? super E> comparator()
返回總的元素個數(shù)(重復(fù)算多個) int size()
返回不同的元素個數(shù) int diffElementSize()
獲取第一個元素 E first()
獲取最后一個元素 E last()
判斷是否包含某個元素 boolean contains(Object o)
清空所有元素 void clear()
添加指定元素(1個) boolean add(E e)
添加指定個數(shù)的特定元素 boolean add(E e, int count)
設(shè)置指定元素的數(shù)量 void setCount(E e, int count)
獲取指定元素的個數(shù) int count(E e)
刪除1個指定元素 boolean remove(Object e)
刪除count個指定元素 boolean remove(E e, int count)
刪除所有的指定元素(不同于clear()) boolean removeAll(Object e)
返回比給定元素嚴(yán)格小的最大元素 E lower(E e)
返回小于或等于給定元素的最大元素 E floor(E e)
返回比給定元素嚴(yán)格大的最小元素 E higher(E e)
返回大于或等于給定元素的最小元素 E ceiling(E e)
刪除指定count的第一個元素 E pollFirst()
刪除1個第一個元素 E pollFirst()
刪除所有count的第一個元素 E pollFirstAll()
刪除1個最后一個元素 E pollLast()
刪除指定count的最后一個元素 E pollLast(int count)
刪除所有count的最后一個元素 E pollLastAll()
TreeMultiSet淺拷貝 Object clone()

代碼演示

這里舉幾個覺得常用的一些方法的調(diào)用方式(當(dāng)然也可以直接閱讀TreeMultiSet源碼, 里頭有詳細(xì)的注釋)引瀑。

1) 新建一個自定義比較器的TreeMultiSet

    TreeMultiSet<Integer> set = new TreeMultiSet<>((o1, o2) -> o2 - o1); // 傳一個從大到小的比較器

2) foreach遍歷所有元素(包含重復(fù)元素)

    for (Integer num : set) {
        // TODO, 這里可輸出類似2, 2, 2, 3這樣的序列
    }

3) 獲取所有元素(包含重復(fù)元素)的正向迭代器

    Iterator<Integer> iterator = set.iterator();
    while (iterator.hasNext()) {
        arr[i++] = iterator.next();
    }

4) 獲取不同元素的正向迭代器

    Iterator<Integer> diffIterator = set.diffIterator();
    while (diffIterator.hasNext()) {
        arr[i++] = diffIterator.next();
    }

5) 獲取所有元素的總個數(shù)(包含重復(fù)元素)

    set.size();

6) 獲取不同元素的個數(shù)

    set.diffElementSize();

7) 獲取第一個元素和最后一個元素

    set.first();
    set.last();

8) 添加指定數(shù)量的元素

    set.add(2, 3); //往集合里頭添加3個2

9) 設(shè)置指定元素的數(shù)量

    set.setCount(2, 3); //設(shè)置集合里頭2的個數(shù)為3個

10) 獲取指定元素的個數(shù)

    set.count(2); //獲取2在集合中的個數(shù)

11) 刪除指定數(shù)量的元素

    set.remove(2, 3); //刪除3個2

12) 刪除指定數(shù)量的第一個元素

    set.pollFirst(2); //刪除2個第一個元素。如集合[1,1,1,2,2,3]榨馁,執(zhí)行這行代碼之后變成[1,2,2,3]

13) 刪除指定數(shù)量的最后一個元素

    set.pollLast(1); //刪除1個最后一個元素憨栽。如集合[1,1,1,2,3,3],執(zhí)行這行代碼之后變成[1,1,1,2,2,3]

單元測試

已經(jīng)對TreeMultiSet的所有方法(包括構(gòu)造函數(shù))都進(jìn)行了單元測試TreeMultiSetTest翼虫,測試覆蓋率達(dá)到100%屑柔。

Coverage

各種集合對比

說明,以下列舉的復(fù)雜度均指時間復(fù)雜度珍剑。并且以下插入刪除操作均指對中間元素的操作掸宛。同時,計算LinkedList的插入和刪除時間復(fù)雜度的時候考慮了查詢到要插入或刪除的位置的時間次慢。

集合 是否可重復(fù) 是否有序 插入復(fù)雜度 刪除復(fù)雜度 獲取最大最小復(fù)雜度
ArrayList O(n) O(n) O(n)
LinkedList O(n) O(n) O(n)
HashSet O(1) O(1) O(n)
TreeSet O(log n) O(log n) O(log n)
PriorityQueue O(log n) O(n) O(log n)
TreeMultiSet O(log n) O(log n) O(log n)

由上表可以發(fā)現(xiàn)本庫實現(xiàn)的TreeMultiSet功能最強大旁涤,在保證可重復(fù)有序的情況下,持頻繁插入刪除操作的時間復(fù)雜度都可以達(dá)到O(log n)的級別迫像。當(dāng)然劈愚,應(yīng)該具體問題具體分析。比如闻妓,沒有remove中間某個元素且需要可重復(fù)元素有序的情況下菌羽,PriorityQueue(底層實現(xiàn)是堆)的性能最佳。

最后

覺得還不錯的話由缆,就點個star支持一下咯~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末注祖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子均唉,更是在濱河造成了極大的恐慌是晨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舔箭,死亡現(xiàn)場離奇詭異罩缴,居然都是意外死亡蚊逢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門箫章,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烙荷,“玉大人,你說我怎么就攤上這事檬寂≈粘椋” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵桶至,是天一觀的道長昼伴。 經(jīng)常有香客問我,道長塞茅,這世上最難降的妖魔是什么亩码? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮野瘦,結(jié)果婚禮上描沟,老公的妹妹穿的比我還像新娘。我一直安慰自己鞭光,他們只是感情好吏廉,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惰许,像睡著了一般席覆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汹买,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天佩伤,我揣著相機(jī)與錄音,去河邊找鬼晦毙。 笑死生巡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的见妒。 我是一名探鬼主播孤荣,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼须揣!你這毒婦竟也來了盐股?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤耻卡,失蹤者是張志新(化名)和其女友劉穎疯汁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卵酪,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡涛目,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年秸谢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹肝。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塑煎,靈堂內(nèi)的尸體忽然破棺而出沫换,到底是詐尸還是另有隱情,我是刑警寧澤最铁,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布讯赏,位于F島的核電站,受9級特大地震影響冷尉,放射性物質(zhì)發(fā)生泄漏漱挎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一雀哨、第九天 我趴在偏房一處隱蔽的房頂上張望磕谅。 院中可真熱鬧,春花似錦雾棺、人聲如沸膊夹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽放刨。三九已至,卻和暖如春尸饺,著一層夾襖步出監(jiān)牢的瞬間进统,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工浪听, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留螟碎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓馋辈,卻偏偏與公主長得像抚芦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子迈螟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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

  • 四叉抡、集合框架 1:String類:字符串(重點) (1)多個字符組成的一個序列,叫字符串答毫。生活中很多數(shù)據(jù)的描述都采...
    佘大將軍閱讀 734評論 0 2
  • 概述 集合框架被設(shè)計成要滿足以下幾個目標(biāo): 該框架必須是高性能的褥民。基本集合(動態(tài)數(shù)組洗搂,鏈表消返,樹载弄,哈希表)的實現(xiàn)也必...
    Tian_Peng閱讀 365評論 0 1
  • 概要 64學(xué)時 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,095評論 0 3
  • Java集合框架 Java平臺提供了一個全新的集合框架∧旒眨“集合框架”主要由一組用來操作對象的接口組成宇攻。不同接口描述...
    小石38閱讀 356評論 0 0
  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,367評論 0 5