CopyOnWriteArraySet是基于CopyOnWriteArrayList实现的,它能保证数组里的对象是唯一的。由于在上篇文章中对CopyOnWriteArrayList做了较详细的讲解,这里就简要分析下CopyOnWriteArraySet的原理。
一、代码结构
CopyOnWriteArraySet继承了AbstractSet,它具备Set属性和方法,最重要的是保证集合里对象唯一,这种唯一是基于equals方法比较,后面会看到源码的比较方法。
public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
private final CopyOnWriteArrayList<E> al;
/** 构造器中初始al为CopyOnWriteArrayList */
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
}
二、插入数据
add方法,如果集合中没有相同对象并且插入集合成功才会返回true,其他情况比如集合中已经有相同对象了,那么返回false。另外,判断集合是否有相同对象,是通过遍历集合中的每一个对象比较的,如果集合中对象比较多的话,也是比较耗性能的。
public boolean add(E e) {
// 直接调用al的方法
return al.addIfAbsent(e);
}
/** 以下代码在CopyOnWriteArrayList中 */
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
/** 返回给定对象在数组中的下标,如果大于等于说明数组中有相同对象 */
private static int indexOf(Object o, Object[] elements,
int index, int fence) {
if (o == null) {
// null对象的比较方法,直接看数组中是否有null对象
for (int i = index; i < fence; i++)
if (elements[i] == null)
return i;
} else {
// 非空对象基于equals作比较
for (int i = index; i < fence; i++)
if (o.equals(elements[i]))
return i;
}
return -1;
}
/** 调用该方法说明数组中可能不存在相同的对象,我们向数组中插入新的对象 */
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
// 首先需要加锁
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
/** 快照和当期获取的数组不相等说明加锁之前数组被覆盖了,
我们需要再次判断新的数组中是否有相同的对象 */
if (snapshot != current) {
// 取两个数组长度最小的一个
int common = Math.min(snapshot.length, len);
// 0到commo比较是否有相同的对象
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
// common到len比较是否有相同的对象
if (indexOf(e, current, common, len) >= 0)
return false;
}
// 到这里说明数组中没有相同的对象了,我们就复制数据到新数组,并且添加新的对象
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
三、总结
- CopyOnWriteArraySet我们要掌握它是基于CopyOnWriteArrayList实现的,并且在并发环境下,可以保证Set中的对象唯一。
- 我们需要重点关注add方法,每次调用都要遍历数组判断是否有相同的对象,比较耗性能。其他方法基本都是直接调用CopyOnWriteArrayList的对应方法。
- 使用场景:并发环境多读少写,要求对象唯一。