JUC之CopyOnWriteArraySet

关键词:CopyOnWriteArrayList    CopyOnWriteArraySet  

在《JUC之CopyOnWriteArrayList》中我们学习了CopyOnWriteArrayList,现在我们再来看看CopyOnWriteArraySet。CopyOnWriteArraySet实现了Set接口,与List不同的是,添加到List中的元素是允许重复的,而添加到Set中的数据是不允许重复的,是唯一存在的。 

CopyOnWriteArraySet并没有完全从零开始去实现不重复元素集合的功能,而是依赖了CopyOnWriteArrayList。利用CopyOnWriteArrayList中提供的一些方法来实现其自身的功能。

字段:

private final CopyOnWriteArrayList<E> al;

在CopyOnWriteArraySet中持有一个CopyOnWriteArrayList对象,通过使用CopyOnWriteArrayList中相应的方法来实现其自身的方法。一个CopyOnWriteArraySet对象持有一个CopyOnWriteArrayList对象。

构造器:

在构造器中对CopyOnWriteArrayList进行初始化。

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

方法:

因为CopyOnWriteArraySet的实现借助了CopyOnWriteArrayList。因此CopyOnWriteArraySet的很多方法实现就是在内部调用了CopyOnWriteArrayList对应的方法,将功能实现转发给CopyOnWriteArrayList。比如:

  • size
public int size() {
    return al.size();
}
  • isEmpty
public boolean isEmpty() {
    return al.isEmpty();
}
  • contains
public boolean contains(Object o) {
    return al.contains(o);
}

等等,关于其他的方法感兴趣的同学可自行查看,实现方式都是一样的。

下面来看看CopyOnWriteArraySet中add方法的时候,其使用了CopyOnWriteArrayList中的一个方法,该方法在《JUC之CopyOnWriteArrayList》中并没有介绍。

  • add
public boolean add(E e) {
    return al.addIfAbsent(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方法我们在《JUC之CopyOnWriteArrayList》中已经介绍了,其是去获取元素e在集合中的下标,当集合中存在元素e时,则返回对应的下标值,如果集合中不存在元素e时,则返回-1。所以如果indexOf的返回值>=0的话,表示元素e存在于集合中,此时就不需要将元素添加到集合中了,addIfAbsent方法直接返回false。

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;
    }
    return -1;
}

addIfAbsent(e, snapshot):

执行将元素添加到集合中。该方法的时候,依然延续模式,使用ReentrantLock加锁来保证线程安全,并对数组内容进行拷贝。

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

if (snapshot != current)

snapshot是通过getArray()获取的,而current也是通过getArray()获取的,咋两个数组会有可能不是同一个对象吗?答案是存在这种可能,这种可能就是在snapshot通过getArray获取到数组后,代码还没有执行到current = getArray()时,有其他线程对集合进行了写操作,此时获取到的array对象就不会是同一个了。(关于为什么有了写操作就不是同一个数组对象了,是因为在执行写操作时,会拷贝出一个新的数组对象,然后将新的数组对象赋值给array)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值