(二) 并发容器之 CopyOnWriteArraySet

同样, 用 List 的并发容器,当然也存在 Set 的并发容器,J.U.C 包下同样存在 CopyOnWriteArraySet

CopyOnWriteArraySet

public class CopyOnWriteArraySet<E> extends AbstractSet<E>
    implements java.io.Serializable {

    // 该类的所有操作由 CopyOnWriteArrayList 完成
    private final CopyOnWriteArrayList<E> al;

    // 在实例化 CopyOnWriteArraySet 的时候,其实就是实例化了 CopyOnWriteArrayList
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
    ....
}

当然,这个是一个 Set ,在添加元素的时候需要多判断一下,直接查看他得 add(E) 方法

//发现它是调用了 CopyOnWriteArrayList 的 addIfAbsent(E) 方法
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
    addIfAbsent(e, snapshot);
}
-----------------
// 查看 indexOf,从方法名就可以看出,这是一个得到元素下标的方法
// 至于是哪个元素的下标,根据 Set 的性质,你也猜的出来了
private static int indexOf(Object o, Object[] elements,
                           int index, int fence) {
    if (o == null) {
        for (int i = index; i < fence; i++)
            if (elements[i] == null)
                return i;
    } else {
        for (int i = index; i < fence; i++)
            if (o.equals(elements[i]))
                return i;
    }
    // 就是遍历数组,如果存在相同元素,返回它的下标,如果不存在,返回 -1
    return -1;
}
-------------------------------
// 这里还需要注意,在高并发的情况下, 如果当前线程已经检查完数组,不存在相同元素
// 在返回 -1 后, 有可能另一个线程将数组的元素修改了,此时是否存在相同元素? 不得而知
// 所以在 addIfAbsent(E, Object[]) 方法需要判断一次
// 接着调用 addIfAbsent(E, Object[]) 方法
private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 最新的数组,用于与快照比较
        Object[] current = getArray();
        int len = current.length;
        // snapshot 相当于快照,为已经检查完毕的数组(不是最新的)
        if (snapshot != current) {
            // 如果快照与最新的数组不匹配
            int common = Math.min(snapshot.length, len);// 取最小值
            for (int i = 0; i < common; i++)
                // 两个数组元素比较
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return 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();
    }
}


说到 Set, 再来看看 HashSet

HashSet

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    
    // HashSet 的所有操作都是交给 HashMap 完成
    private transient HashMap<E,Object> map;

    // 由于 HashMap 需要 K-V 两个参数,该常量作为 value 放入 HashMap 
    private static final Object PRESENT = new Object();

    // 同 CopyOnWriteArraySet, 在实例化 HashSet 的时候其实就是实例化了 HashMap
    public HashSet() {
        map = new HashMap<>();
    }
    ....
}

由于 HashMap 的特性 : Key 不唯一,JDK 直接使用 HashMap 来操作 HashSet, 查看 HashSet 的 add 方法

public boolean add(E e) {
    return map.put(e, PRESENT)==null;       
}

就是调用了 HashMap 的 put 方法, 使用一个常量作为 value 放入 HashMap 中

猜你喜欢

转载自blog.csdn.net/Gp_2512212842/article/details/107691003