1、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();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
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) {
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++)
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();
}
}
笔记:上面方法其实也可以如果发现不同直接现行遍历一遍,然后决定时候插入,可是这样在大数据量下性能会降低很多,比如只是修改部分值,添加值,不大范围改动元素位置。上面的算法会减少遍历的数据量,提升性能。但是如果位于较小索引位置的元素被删除,线性遍历的数据量依然很多。
上面方法执行实现的功能是不包含则添加否则返回false,进入私有addIfAbsent说明,列表里面不包含e,但是此时列表元素可能已经被修改,如果没有被修改直接加入一个元素,如果被修改了。修改又可能是添加 删除 修改,上面已经说了,如果是修改
if (current[i] != snapshot[i] && eq(e, current[i])) return false;就判断修改的值是否和插入的值是否相等。if (indexOf(e, current, common, len) >= 0) return false;这段代码是往后找是否被其他线程已经插入e元素。
2、remove(Object o)
public boolean remove(Object o) {
Object[] snapshot = getArray();
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;
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();
}
}
笔记:上述是在列表里面移除摸个元素
同addIfAbsent其实列表如果被修改也可以线性遍历,然后在判断是否移除元素,同样基于性能有了上述算法。
能进入私有remove方法说明快照列表里面包含o元素并且满足snapshot[index] = o
,此时列表如果没有被修改直接复制元素删掉o即可。
如果列表被其他线程修改了,新列表长度无非小于等于index,或者大于index,如果只是简单的更新元素位置没有变动, if (current[i] != snapshot[i] && eq(o, current[i])) {
index = i;
break findIndex;
}
这段代码快速定位,如果是大范围变动,遍历依然很耗时。
如果上述没有找到o元素,if (index >= len) return false;这段代码判断索引是否超出最新列表长度,如果超出到这结束,没有找到。否则index在最新列表的索引范围内查看以下 if (current[index] == o),就是之前位置上是否还是o元素,如果还不是就遍历index之后的元素找o元素