在上一篇文章中介绍了CopyOnWriteArrayList,既然有线程安全的List,那必定会有线程安全的Set。J.U.C包下存在一个线程安全的Set,它与CopyOnWriteArrayList名称类似,它是CopyOnWriteArraySet,那它是如何保证线程安全的呢,接下来解读一下相关的源码
构造函数
CopyOnWriteArraySet中有两个构造函数,如下代码所示
public class CopyOnWriteArraySet<E> extends AbstractSet<E> implements java.io.Serializable {
//序列号
private static final long serialVersionUID = 5457747651344034263L;
//CopyOnWriteArrayList对象引用,
//其实CopyOnWriteArraySet直接引用了CopyOnWriteArrayList来实现Set这个数据结构
private final CopyOnWriteArrayList<E> al;
/**
* 默认构造函数,创建一个大小为0的数组
*/
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}
/**
* 通过集合类来创建CopyOnWriteArraySet对象
*/
public CopyOnWriteArraySet(Collection<? extends E> c) {
//先判断集合类是否是CopyOnWriteArraySet类,若是则取出其中的CopyOnWriteArrayList对象
if (c.getClass() == CopyOnWriteArraySet.class) {
@SuppressWarnings("unchecked")
CopyOnWriteArraySet<E> cc = (CopyOnWriteArraySet<E>)c;
al = new CopyOnWriteArrayList<E>(cc.al);
}
else {
//若不是,则调用addAllAbsent方法添加元素
al = new CopyOnWriteArrayList<E>();
//直接调用CopyOnWriteArrayList中的方法来构建
al.addAllAbsent(c);
}
}
}
从上面的源码可知,CopyOnWriteArraySet其实就是直接使用了CopyOnWriteArrayList来构建的,它的内部并没有添加较多的操作(Set集合的子类好像都是这样,没有自己的实现,直接用现成的数据结构)。
其中addAllAbsent方法的源码如下(一下提及的方法都是CopyOnWriteArrayList内部实现好的)
public int addAllAbsent(Collection<? extends E> c) {
//判断添加的集合是否长度为0,若不是,则进行下一步。
Object[] cs = c.toArray();
if (cs.length == 0)
return 0;
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
int added = 0;
// uniquify and compact elements in cs
for (int i = 0; i < cs.length; ++i) {
Object e = cs[i];
//判断是否有重复元素,若有重复元素,则忽略这个元素
if (indexOf(e, elements, 0, len) < 0 &&
indexOf(e, cs, 0, added) < 0)
cs[added++] = e;
}
//当元素个数大于0才执行复制替换的流程
if (added > 0) {
Object[] newElements = Arrays.copyOf(elements, len + added);
System.arraycopy(cs, 0, newElements, len, added);
setArray(newElements);
}
return added;
} finally {
//解锁
lock.unlock();
}
}
Set集合需要保证元素唯一,所以需要对每个元素进行判断去重,再将其插入集合中。
add方法
add()方法源码如下
public boolean add(E e) {
return al.addIfAbsent(e);
}
addIfAbsent方法源码如下
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
//判断是否已经存在该元素,若存在直接返回,不存在则进行下一步。
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false : addIfAbsent(e, snapshot);
}
/**
* 方法主体
*/
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) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
//若第i个元素不同且元素值等于要插入的值,直接返回false.
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
//比较完成,要插入元素已经存在数组中就直接返回false.(避免上诉循环未找到重复元素而导致插入重复值)
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();
}
}
CopyOnWriteArrayList中提供了一系列方法来保证数组元素的唯一性,包证Set集合的特性,相关注释在源码上都有,就不再重复了
remove方法
public boolean remove(Object o) {
return al.remove(o);
}
直接调用CopyOnWriteArrayList中的remove方法
CopyOnWriteArraySet没有get和set方法,所以CopyOnWriteArraySet特别简单,但由于CopyOnWriteArraySet要保证Set的特性,所以在进行元素添加时不仅耗费空间,还要耗费一定的时间用于判断是否存在重复元素,所以CopyOnWriteArraySet执行效率比CopyOnWriteArrayList低
其他方法
public int size() {
return al.size();
}
public boolean isEmpty() {
return al.isEmpty();
}
public boolean contains(Object o) {
return al.contains(o);
}
public Object[] toArray() {
return al.toArray();
}
public <T> T[] toArray(T[] a) {
return al.toArray(a);
}
public void clear() {
al.clear();
}