1. HashSet
HashSet实际上是一个HashMap实例,都是一个存放链表的数组。
它不保证存储元素的迭代顺序;此类允许使用null元素。
HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象
private static final Object PRESENT = new Object();
如下图所示,底层的map的value值都是同一个obj,map的key存储我们的hashset:
当迭代hashset的时候,本质就是迭代hashmap的keys:
public Iterator<E> iterator() {
return map.keySet().iterator();
}
2. CopyOnWriteArraySet
2.1继承关系
它是线程安全的无序的集合,可以将它理解成线程安全的HashSet
。
有意思的是,CopyOnWriteArraySet和HashSet虽然都继承于共同的父类AbstractSet;但是,HashSet是通过“散列表(HashMap)”实现的,而CopyOnWriteArraySet则是通过“动态数组(CopyOnWriteArrayList)”实现的,并不是散列表。
和CopyOnWriteArrayList类似,其实CopyOnWriteSet底层包含一个CopyOnWriteList,几乎所有操作都是借助CopyOnWriteList,就像HashSet包含HashMap。
2.2. 特性
CopyOnWriteArraySet具有以下特性:
- 它最适合于具有以下特征的应用程序:Set 大小通常保持很小,
读多写少的操作
,需要在遍历期间防止线程间的冲突。
CopyOnWriteList的适应场景就是读多写少,因为利用复制新数组模式,如果写多的话,会频繁产生复制操作,影响资源。 - 它是
线程安全
的。
CopyOnWriteList本身是线程安全的 - 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
- 迭代器支持hasNext(), next()等不可变操作,但不支持可变 remove()等 操作。
- 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
2.3. 源码
public boolean add(E e) {
//这个al就是CopyOnWriteArrayList,也就是说CopyOnWriteArraySet内部是用CopyOnWriteArrayList来实现的
return al.addIfAbsent(e);
}
CopyOnWriteArraySet是通过CopyOnWriteArrayList实现的,它的API基本上都是通过调用CopyOnWriteArrayList
的API来实现的。相信对CopyOnWriteArrayList了解的话,对CopyOnWriteArraySet的了解是水到渠成的事;所以,这里就不再对CopyOnWriteArraySet的代码进行详细的解析了