(四)并发-集合SET(CopyOnWriteArraySet)

165 阅读3分钟

CopyOnWriteArraySetHashSet 都是不允许重复元素的集合,CopyOnWriteArraySet 线程安全,HashSet 线程不安全,CopyOnWriteArraySet 底层是基于动态数组实现,并通过CopyOnWriteArrayList 相关方法操作,HashSet 是基于散列表实现。 CopyOnWriteArraySet 是无序的,而CopyOnWriteArrayList 是有序的,当多个线程对CopyOnWriteArraySet 进行写操作时,首先也是获取相关的锁资源权限才能进行写操作,也是复制一份样本进行,写完成通过地址引用重新赋值给原始变量。同时释放锁资源,当然在写操作时可以进行读并发不阻塞,但是不能保证数据的实时性,由于写操作会进行复制,所以会造成空间的一定浪费,故也是使用在写少读多的并发场景。

继承关系

图片.png

Iterable 说明具有迭代器的功能
Collection 说明含有集合基本的方法
Set 拥有Set集合的所有方法
AbstractSet 重写removeAll方法

初始化方式

//有参
public CopyOnWriteArraySet(Collection<? extends E> c)
//无参
public CopyOnWriteArraySet()

方式一/无参

CopyOnWriteArraySet 内部维护了一个CopyOnWriteArrayList的al变量,调用无参构造进行实例化CopyOnWriteArraySet 时,直接创建一个CopyOnWriteArrayList 并赋值给 al 变量

public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
}

方式二/有参

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);
    }
}

首先传入集合类型的 c 变量判断是不是CopyOnWriteArraySet 类型,如果是则直接强制性转换成CopyOnWriteArraySet 并调用CopyOnWriteArrayList 有参构造创建集合并 将 c 添加进去。如果不是CopyOnWriteArraySet 类型,初始化CopyOnWriteArrayList 通过方法addAllAbsent 将c集合中的数据进行去重并添加。

CopyOnWriteArrayList.addAllAbsent()方法

public int addAllAbsent(Collection<? extends E> c) {
    //将集合c转换成数组
    Object[] cs = c.toArray();
    //如果 c 不是 ArrayList 类型 进行拷贝
    if (c.getClass() != ArrayList.class) {
        cs = cs.clone();
    }
    //如果 cs 为空 结束方法 
    if (cs.length == 0)
        return 0;
    //获取锁资源权限
    synchronized (lock) {
        //获取CopyOnWriteArrayList原始集合数组
        Object[] es = getArray();
        int len = es.length;
        int added = 0;
        // uniquify and compact elements in cs
        for (int i = 0; i < cs.length; ++i) {
            Object e = cs[i];
            //说明元素在 es 集合中不存在
            if (indexOfRange(e, es, 0, len) < 0 && indexOfRange(e, cs, 0, added) < 0)
                //赋值给 added 索引 added 进行+1
                cs[added++] = e;
        }
        //说明已经去重了
        if (added > 0) {
            //创建新数组(长度为len + added)并拷贝 es
            Object[] newElements = Arrays.copyOf(es, len + added);
            //将 cs 数据拷贝到 newElements 中
            System.arraycopy(cs, 0, newElements, len, added);
            //重新设置array引用为 newElements 地址
            setArray(newElements); 
        }
        //返回添加的数据个数
        rturn added;
    }
    //释放锁资源
}

添加数据

CopyOnWriteArraySet.add(E e) -> CopyOnWriteArrayList.addIfAbsent(e) -> 
CopyOnWriteArrayList.addIfAbsent(E e, Object[] snapshot)
//简单方法
public boolean add(E e) {
    return al.addIfAbsent(e);
}
public boolean addIfAbsent(E e) {
    //获取当前的数组对象快照
    Object[] snapshot = getArray();
    //如果元素e在snapshot中的不存在并且 添加成功返回成功标记
    return indexOfRange(e, snapshot, 0, snapshot.length) < 0 && addIfAbsent(e, snapshot);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
    //获取锁资源权限
    synchronized (lock) {
        //获取当前数组
        Object[] current = getArray();
        int len = current.length;
        //如果当前快照不是 当前数组
        if (snapshot != current) {
            // 取最小值
            int common = Math.min(snapshot.length, len);
            //判断元素e在current数组中是否存在 如果存在 直接返回不添加
            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);
        //将新值放到len的位置
        newElements[len] = e;
        //将心的引用复制给array
        setArray(newElements);
        //返回成功标记
        return true;
    }
    //释放锁资源
}

删除数据

remove(Object o) -> CopyOnWriteArrayList.remove(Object o)
public boolean remove(Object o) {
    //获取原始数组的快照
    Object[] snapshot = getArray();
    //获取元素O在原始快照中的索引
    int index = indexOf(o, snapshot, 0, snapshot.length);
    //移除元素
    return (index < 0) ? false : remove(o, snapshot, index);
}
private boolean remove(Object o, Object[] snapshot, int index) {
    //获取资源锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //获取当前数组
        Object[] current = getArray();
        //当前数组的长度
        int len = current.length;
        //如果当前数组不是快照 查找O元素在新数组中的索引
        if (snapshot != current) findIndex: {
            int prefix = Math.min(index, len);
            for (int i = 0; i < prefix; i++) {
                if (current[i] != snapshot[i] && eq(o, current[i])) {
                    index = i;
                    break findIndex;
                }
            }
            if (index >= len)
                return false;
            if (current[index] == o)
                break findIndex;
            index = indexOf(o, current, index, len);
            if (index < 0)
                return false;
        }
        //重新创建数组
        Object[] newElements = new Object[len - 1];
        //赋值数组
        System.arraycopy(current, 0, newElements, 0, index);
        System.arraycopy(current, index + 1, newElements, index, len - index - 1);
        //数组索引从新指向
        setArray(newElements);
        return true;
    } finally {
        //释放锁资源
        lock.unlock();
    }
}

遍历数据

public Iterator<E> iterator()
public void forEach(Consumer<? super E> action)
forEach(Consumer<? super E> action) -> CopyOnWriteArrayLis.forEach(Consumer<? super E> action)
public void forEach(Consumer<? super E> action) {
    //如果集合是空则抛出异常
    if (action == null) throw new NullPointerException();
    //获取袁术数组
    Object[] elements = getArray();
    int len = elements.length;
    //进行迭代
    for (int i = 0; i < len; ++i) {
        @SuppressWarnings("unchecked") E e = (E) elements[i];
        //消费型接口
        action.accept(e);
    }
}