JAVA集合 - SET

set.png

对集合的操作

对集合主要有三种操作:

  1. 插入和删除,以及在指定位置插入和删除
  2. 获取元素
  3. 对集合进行迭代

不同的集合在这三个方面有不同的性能,没有哪个集合是完美的。

数据结构

实现集合的四种数据结构:

  1. 数组(Arrays)
    优点:根据位置(positon)快速访问元素。
    缺点:插入和删除元素慢,因为需要重新调整元素位置。
    如:ArrayList, CopyOnWriteArrayList, EnumSet, EnumMap

  2. 链表(Linked list)
    优点:插入和删除元素快。
    缺点:根据位置访问元素慢,因为需要从头到尾向下找。
    如:ConcurrentSkipListSet, LinkedList, ConcurrentLinkedQueue, LinkedBlockingQueue, ConcurrentSkipListMap

  3. 哈希表(Hash tables),根据内容进行索引,不像数组和链表提供基于位置的访问。
    优点:根据内容访问元素,以及插入和删除都比较快。
    缺点:无法根据位置索引访问元素。
    如:HashSet, LinkedHashSet, HashMap, LinkedHashMap, WeakHashMap, IdentityHashMap, ConcurrentHashMap

  4. 树(Trees)
    根据内容管理其元素,根据指定的排序方法保存元素。
    优点:有序,插入和删相对较快。
    缺点:无法根据位置索引访问元素。
    如:TreeSet, TreeMap

具体的集合实现可能用到多种数据结构,比如在JAVA8中,HashMap就可能用到链表,也可能用到树。

集合的并发访问

当一个集合被并发访问,那么就要处理线程安全的问题,JAVA提供了几种机制来实现安全的并发访问:

  1. Synchronized
    比如:Hashtable, Vector,其所有方法都是同步的,导致并发性能极差,应该避免使用。
    同样还有Collections.synchronized下的系列同步包装方法。
    List<Object> synchronizedList = Collections.synchronizedList(new ArrayList<>());
    
  2. copy-on-write
    使用数组存储集合元素,并且数组是immutable的,任何对集合的修改(新增/删除),都是导致新数组的创建,
    需要同步的仅仅是新数组替换旧数组,是通过将变量声明为volatile来实现的。
    这种类型的集合适合使用场景,因数不需要同步也不需要数组复制。
    比如:CopyOnWriteArraySet, CopyOnWriteArrayList
  3. CAS(compare and swap)
    CAS其字面意思就是比较并替换,其大概过程是:首先复制变量的值,再开始其要做的计算,当计算完成需要更新变量的值时,会先比较此时变量的值是否和开始时复制的变量一致,如果一致说明没有被其他线程修改过,那就可以完成更新。如果不一致,则说明已经被其他线程修改过了,可以重新计算也可以放弃,根据具体算法需要决定。
    CAS有ABA问题,可以通过AtomicStampedReference解决,这里就不展开了。
    比如:ConcurrentLinkedQueue, ConcurrentSkipListMap
  4. java.util.concurrent.locks.Lock
    JAVA中的锁除了synchronized,还有ReentrantLock, ReentrantReadWriteLock, StampedLock
    比如:LinkedBlockingQueue就利用ReentrantLock来保证同步。至于各种锁的区别,此处就不展开了,如有兴趣Google一下。

SET

SortedSet (java.util)
    NavigableSet (java.util)
        ConcurrentSkipListSet (java.util.concurrent)
        TreeSet (java.util)
AbstractSet (java.util)
    TreeSet (java.util)
    HashSet (java.util)
        LinkedHashSet (java.util)
    EnumSet (java.util)
        JumboEnumSet (java.util)
        RegularEnumSet (java.util)
    CopyOnWriteArraySet (java.util.concurrent)
    ConcurrentSkipListSet (java.util.concurrent)

HashSet, TreeSet, LinkedHashSet

Set中的元素不能重复,其实现主要有三种:HashSet, TreeSet, LinkedHashSet,简单来说如果想要fast set就使用HashSet;如果需要有序使用TreeSet;如果想保持插入顺序使用LinkedHashSet.

HashSet LinkedHashSet TreeSet
如何实现? 使用HashMap实现 使用LinkedHashMap实现 使用TreeMap实现
元素顺序 没有任何顺序 保持插入顺序 根据提供的Comparator排序
性能 A (优) A--(良) C (差)
是否允许NULL 最多允许一个NULL 最多允许一个NULL JAVA7之前允许第一个元素为NULL,从7开始不允许有NULL
如果判断元素是否存在 equals() and hashCode() equals() and hashCode() compare() or compareTo() 比较结果为0即为相同
时间复杂度 O(1) O(1) O(log(n))

他们的相同点:

  1. 都不允许元素重复
  2. 都不是线程安全的
  3. 都是fail-fast的,如果在创建Iterator之后,修改集合会抛出ConcurrentModificationException。

http://javaconceptoftheday.com/hashset-vs-linkedhashset-vs-treeset-in-java/
http://www.benchresources.net/hashset-vs-linkedhashset-vs-treeset-in-java/


CopyOnWriteArraySet

CopyOnWriteArraySet是通过CopyOnWriteArrayList实现的,其底层实现是数组,且该数组是不可变的,如果要修改CopyOnWriteArraySet的内容就需要重新创建新的数组。
如果需要频繁修改Set,不应该使用CopyOnWriteArraySet。在对CopyOnWriteArraySet, CopyOnWriteArrayList进行遍历时,遍历的只是底层数组的快照,而且该Iterator不支持remove方法。

EnumSet

此集合用来存放枚举类型,并且只能存放同一种枚举类型,其底层是基与位操作(bit manipulation)来实现,所以性能很高,即便是containsAll, retainAll这类操作依然很快。永远不会抛出ConcurrentModificationException,不是线程安全的,如果需要线程同步,则:

Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));

The iterators of EnumSet are weakly consistent.

ConcurrentSkipListSet

基与跳表(skip list)实现的并发集合,是通过ConcurrentSkipListMap实现的,类似TreeSet该集合也会对元素进行排序,支持并发操作,通过CAS实现的。
跳表有多层链表,最底层存放了所有的元素,如果机率(probability)设置为0.5,那么上一层会存放下一层一半数量的元素,此时如果有新的元素插入,首先会插入最底层,是否插入上一层会投币决定是否插入。

image

The iterators of ConcurrentSkipListSet are weakly consistent.

Skip list1
Skip list2

Set集合的时间复杂度

add contains remove next notes 是否允许为NULL
HashSet O(1) O(1) O(1) O(h/n) h是hash表容量 最多一个NULL
LinkedHashSet O(1) O(1) O(1) O(1) 最多一个NULL
CopyOnWriteArraySet O(n) O(n) O(n) O(1) 最多一个NULL
EnumSet O(1) O(1) O(1) O(1) 不允许
TreeSet O(log n) O(log n) O(log n) O(log n) 7之前允许第一个元素为NULL,从7开始不允许为NULL
ConcurrentSkipListSet O(log n) O(log n) O(log n) O(1) 不允许

https://docs.oracle.com/javase/8/docs/technotes/guides/collections/changes8.html
https://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • Java集合类可用于存储数量不等的对象,并可以实现常用的数据结构如栈,队列等,Java集合还可以用于保存具有映射关...
    小徐andorid阅读 1,939评论 0 13
  • Java集合 ★★★★★集合框架:用于存储数据的容器。 特点: 1:对象封装数据,对象多了也需要存储。集合用于存储...
    毛子果阅读 702评论 0 1
  • 本系列出于AWeiLoveAndroid的分享,在此感谢,再结合自身经验查漏补缺,完善答案。以成系统。 Java基...
    济公大将阅读 1,528评论 1 6
  • 缺少职业意识的人。一个职业是人们对所从事的职业认同感,能够最大限度的调动人们的积极性和创新性。缺少执业意识的人怎么...
    青春无悔8888阅读 765评论 0 1
  • 准备工作: 1、买菜。下班顺路到超市,已经没剩什么新鲜蔬菜了,倒是味道微苦的油麦菜还有不少,选了两棵。 2、处理主...
    读云轩札记阅读 409评论 0 1