在上一篇文章中介绍了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(); }