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