【从入门到放弃-Java】并发编程-JUC-CopyOnWriteArraySet

前言

CopyOnWriteArraySet也是JUC下常用容器,其底层实现是基于CopyOnWriteArrayList的,关于CopyOnWriteArrayList的详情可以查看【从入门到放弃-Java】并发编程-JUC-CopyOnWriteArrayList,接下来我们看下源码。

CopyOnWriteArraySet

CopyOnWriteArraySet

/**
 * Creates an empty set.
 */
 //简单粗暴,只有一个成员变量al,是CopyOnWriteArrayList类型的,初始化时,new一个CopyOnWriteArrayList赋值给al。
public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
}

/**
 * Creates a set containing all of the elements of the specified
 * collection.
 *
 * @param c the collection of elements to initially contain
 * @throws NullPointerException if the specified collection is null
 */
public CopyOnWriteArraySet(Collection<? extends E> c) {
    if (c.getClass() == CopyOnWriteArraySet.class) {
        @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
            (CopyOnWriteArraySet<E>)c;
        al = new CopyOnWriteArrayList<E>(cc.al);
    }
    else {
        al = new CopyOnWriteArrayList<E>();
        al.addAllAbsent(c);
    }
}

add

/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element {@code e} to this set if
 * the set contains no element {@code e2} such that
 * {@code Objects.equals(e, e2)}.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns {@code false}.
 *
 * @param e element to be added to this set
 * @return {@code true} if this set did not already contain the specified
 *         element
 */
public boolean add(E e) {
    //调用CopyOnWriteArrayList的addIfAbsent方法。
    return al.addIfAbsent(e);
}

//看下CopyOnWriteArrayList的addIfAbsent方法如何实现。
/**
 * Appends the element, if not present.
 *
 * @param e element to be added to this list, if absent
 * @return {@code true} if the element was added
 */
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    //先查找e是否在存在,不存在的话 调用addIfAbsent(E e, Object[] snapshot)方法
    return indexOfRange(e, snapshot, 0, snapshot.length) < 0
        && addIfAbsent(e, snapshot);
}

/**
 * A version of addIfAbsent using the strong hint that given
 * recent snapshot does not contain e.
 */
private boolean addIfAbsent(E e, Object[] snapshot) {
    //加锁
    synchronized (lock) {
        Object[] current = getArray();
        int len = current.length;
        //加锁后check下快照是否被改动
        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++)
                if (current[i] != snapshot[i]
                    && Objects.equals(e, current[i]))
                    return false;
            if (indexOfRange(e, current, common, len) >= 0)
                    return false;
        }
        //验证数组中没有此元素,则在末尾添加。
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        //替换原数组。
        setArray(newElements);
        return true;
    }
}

addAll

/**
 * Adds all of the elements in the specified collection to this set if
 * they're not already present.  If the specified collection is also a
 * set, the {@code addAll} operation effectively modifies this set so
 * that its value is the <i>union</i> of the two sets.  The behavior of
 * this operation is undefined if the specified collection is modified
 * while the operation is in progress.
 *
 * @param  c collection containing elements to be added to this set
 * @return {@code true} if this set changed as a result of the call
 * @throws NullPointerException if the specified collection is null
 * @see #add(Object)
 */
public boolean addAll(Collection<? extends E> c) {
    //直接调用CopyOnWriteArrayList的addAllAbsent方法。
    return al.addAllAbsent(c) > 0;
}

/**
 * Appends all of the elements in the specified collection that
 * are not already contained in this list, to the end of
 * this list, in the order that they are returned by the
 * specified collection's iterator.
 *
 * @param c collection containing elements to be added to this list
 * @return the number of elements added
 * @throws NullPointerException if the specified collection is null
 * @see #addIfAbsent(Object)
 */
public int addAllAbsent(Collection<? extends E> c) {
    //将要添加的元素变为数组
    Object[] cs = c.toArray();
    if (cs.length == 0)
        return 0;
    //加锁
    synchronized (lock) {
        Object[] es = getArray();
        int len = es.length;
        int added = 0;
        // uniquify and compact elements in cs
        //遍历要添加的数组,如果在List的数组中不存在此元素,则添加到cs数组中
        for (int i = 0; i < cs.length; ++i) {
            Object e = cs[i];
            if (indexOfRange(e, es, 0, len) < 0 &&
                indexOfRange(e, cs, 0, added) < 0)
                cs[added++] = e;
        }
        if (added > 0) {
            Object[] newElements = Arrays.copyOf(es, len + added);
            //将cs数组的前added个元素添加在List数组后面
            System.arraycopy(cs, 0, newElements, len, added);
            //替换原数组
            setArray(newElements);
        }
        return added;
    }
}

remove

/**
 * Removes the specified element from this set if it is present.
 * More formally, removes an element {@code e} such that
 * {@code Objects.equals(o, e)}, if this set contains such an element.
 * Returns {@code true} if this set contained the element (or
 * equivalently, if this set changed as a result of the call).
 * (This set will not contain the element once the call returns.)
 *
 * @param o object to be removed from this set, if present
 * @return {@code true} if this set contained the specified element
 */
public boolean remove(Object o) {
    //直接调用CopyOnWriteArrayList的remove
    return al.remove(o);
}

总结

通过源码分析,我们了解到,CopyOnWriteArraySet主要是依赖CopyOnWriteArrayList来实现各方法的。
因此与CopyOnWriteArrayList一样,更适用于读多写少的并发操作中。
详细原因在【从入门到放弃-Java】并发编程-JUC-CopyOnWriteArrayList中以及解释过了,这里不再赘述。

值得一提的是,常用的非并发容器HashSet,是基于HashMap实现的,利用HashMap中Entry的key做到唯一值,Entry的value是一个不可变静态对象Object。但是JUC中并发Set是却不是基于Map的,学习完这三章并发容器,你能回答这是为什么吗?可以在评论中留言,我们一起探讨下哦~

更多文章

见我的博客:https://nc2era.com

written by AloofJr,转载请注明出处

上一篇:【从入门到放弃-Java】并发编程-JUC-ConcurrentLinkedQueue


下一篇:【从入门到放弃-Java】并发编程-线程安全