海南网站建设中心,wordpress 删除页眉,asp网站开发视频,wordpress获取4条文章标题二、CopyOnWriteArrayList
2.1 CopyOnWriteArrayList介绍
CopyOnWriteArrayList是一个线程安全的ArrayList。
CopyOnWriteArrayList是基于lock锁和数组副本的形式去保证线程安全。
在写数据时#xff0c;需要先获取lock锁#xff0c;需要复制一个副本数组#xff0c;将数…二、CopyOnWriteArrayList
2.1 CopyOnWriteArrayList介绍
CopyOnWriteArrayList是一个线程安全的ArrayList。
CopyOnWriteArrayList是基于lock锁和数组副本的形式去保证线程安全。
在写数据时需要先获取lock锁需要复制一个副本数组将数据插入到副本数组中将副本数组赋值给CopyOnWriteArrayList中的array。
因为CopyOnWriteArrayList每次写数据都要构建一个副本如果你的业务是写多并且数组中的数据量比较大尽量避免去使用CopyOnWriteArrayList因为这里会构建大量的数组副本比较占用内存资源。
CopyOnWriteArrayList是弱一致性的写操作先执行但是副本还有落到CopyOnWriteArrayList的array属性中此时读操作是无法查询到的。
2.2 核心属性方法
主要查看2个核心属性以及2个核心方法还有无参构造
/** 写操作时需要先获取到的锁资源CopyOnWriteArrayList全局唯一的。 */
final transient ReentrantLock lock new ReentrantLock();/** CopyOnWriteArrayList真实存放数据的位置查询也是查询当前array */
private transient volatile Object[] array;// 获取array属性
final Object[] getArray() {return array;
}// 替换array属性
final void setArray(Object[] a) {array a;
}/*** 默认new的CopyOnWriteArrayList数组长度为0。* 不像ArrayList初始长度是10每次扩容1/2, CopyOnWriteArrayList不存在这个概念* 每次写的时候都会构建一个新的数组*/
public CopyOnWriteArrayList() {setArray(new Object[0]);
}2.3 读操作
CopyOnWriteArrayList的读操作就是get方法基于数组索引位置获取数据。
方法之所以要差分成两个是因为CopyOnWriteArrayList中在获取数据时不单单只有一个array的数组需要获取值还有副本中数据的值。
// 查询数据时只能通过get方法查询CopyOnWriteArrayList中的数据
public E get(int index) {// getArray拿到array数组调用get方法的重载return get(getArray(), index);
}
// 执行get(int)时内部调用的方法
private E get(Object[] a, int index) {// 直接拿到数组上指定索引位置的值return (E) a[index];
}2.4 写操作
CopyOnWriteArrayList是基于lock锁和副本数组的形式保证线程安全。
// 写入元素不指定索引位置直接放到最后的位置
public boolean add(E e) {// 获取全局锁并执行lockfinal ReentrantLock lock this.lock;lock.lock();try {// 获取原数组还获取了原数组的长度Object[] elements getArray();int len elements.length;// 基于原数组复制一份副本数组并且长度比原来多了一个Object[] newElements Arrays.copyOf(elements, len 1);// 将添加的数据放到副本数组最后一个位置newElements[len] e;// 将副本数组赋值给CopyOnWriteArrayList的原数组setArray(newElements);// 添加成功返回truereturn true;} finally {// 释放锁~lock.unlock();}
}// 写入元素指定索引位置。不会覆盖数据
public void add(int index, E element) {// 拿锁加锁~final ReentrantLock lock this.lock;lock.lock();try {// 获取原数组还获取了原数组的长度Object[] elements getArray();int len elements.length;// 如果索引位置大于原数组的长度或者索引位置是小于0的。if (index len || index 0)throw new IndexOutOfBoundsException(Index: index, Size: len);// 声明了副本数组Object[] newElements;// 原数组长度 - 索引位置等到numMovedint numMoved len - index;// 如果numMoved为0说明数据要放到最后面的位置if (numMoved 0)// 直接走了原生态的方式正常复制一份副本数组newElements Arrays.copyOf(elements, len 1);else {// 数组要插入的位置不是最后一个位置// 副本数组长度依然是原长度 1newElements new Object[len 1];// 将原数组从0索引位置开始复制复制到副本数组中的前置位置System.arraycopy(elements, 0, newElements, 0, index);// 将原数组从index位置开始复制复制到副本数组的index 1往后放。// 这时index就空缺出来了。System.arraycopy(elements, index, newElements, index 1,numMoved);}// 数据正常放到指定的索引位置newElements[index] element;// 将副本数组赋值给CopyOnWriteArrayList的原数组setArray(newElements);} finally {// 释放锁lock.unlock();}
}2.5 移除数据
关于remove操作要分析两个方法 基于索引位置移除指定数据 基于具体元素删除数组中最靠前的数据 当前这种方式嵌套了一层导致如果元素存在话成本是比较高的。如果元素不存在这种设计不需要加锁提升写的效率
// 删除指定索引位置的数据
public E remove(int index) {// 拿锁加锁final ReentrantLock lock this.lock;lock.lock();try {// 获取原数组和原数组长度Object[] elements getArray();int len elements.length;// 通过get方法拿到index位置的数据E oldValue get(elements, index);// 声明numMovedint numMoved len - index - 1;// 如果numMoved为0说明删除的元素是最后的位置if (numMoved 0)// Arrays.copyOf复制一份新的副本数组并且将最后一个数据不要了// 基于setArray将副本数组赋值给array原数组setArray(Arrays.copyOf(elements, len - 1));else {// 删除的元素不在最后面的位置// 声明副本数组长度是原数组长度 - 1Object[] newElements new Object[len - 1];// 从0开始复制的index前面System.arraycopy(elements, 0, newElements, 0, index);// 从index后面复制到最后System.arraycopy(elements, index 1, newElements, index,numMoved);setArray(newElements);}// 返回被干掉的数据return oldValue;} finally {// 释放锁lock.unlock();}
}// 删除元素最前面的
public boolean remove(Object o) {// 没加锁// 获取原数组Object[] snapshot getArray();// 用indexOf获取元素在数组的哪个索引位置// 没找到的话返回-1int index indexOf(o, snapshot, 0, snapshot.length);// 如果index 0,说明元素没找到直接返回false告辞// 如果找到了元素的位置直接执行remove方法的重载return (index 0) ? false : remove(o, snapshot, index);
}
// 执行remove(Object o)找到元素位置时执行当前方法
private boolean remove(Object o, Object[] snapshot, int index) {// 拿锁加锁final ReentrantLock lock this.lock;lock.lock();try {// 拿到原数组和长度Object[] current getArray();int len current.length;// findIndex: 是给if起标识break 标识的时候直接跳出if的代码块~~if (snapshot ! current) findIndex: {// 如果没进到if说明数组没变化按照原来的index位置删除即可// 进到这说明数组有变化之前的索引位置不一定对// 拿到index位置和原数组长度的值int prefix Math.min(index, len);// 循环判断数组变更后是否影响到了要删除元素的位置for (int i 0; i prefix; i) {// 如果不相等说明元素变化了。// 同时判断变化的元素是否是我要删除的元素oif (current[i] ! snapshot[i] eq(o, current[i])) {// 如果满足条件说明当前位置就是我要删除的元素index i;break findIndex;}}// 如果for循环结束没有退出if说明元素可能变化了总之没找到要删除的元素// 如果删除元素的位置已经大于等于数组长度了。if (index len)// 超过索引范围了没法删除了。return false;// 索引还在范围内判断是否是还是原位置如果是直接跳出if代码块if (current[index] o)break findIndex;// 重新找元素在数组中的位置index indexOf(o, current, index, len);// 找到直接跳出if代码块// 没找到。执行下面的return falseif (index 0)return false;}// 删除套路构建新数组复制index前的复制index后的Object[] newElements new Object[len - 1];System.arraycopy(current, 0, newElements, 0, index);System.arraycopy(current, index 1,newElements, index,len - index - 1);// 复制到arraysetArray(newElements);// 返回true删除成功return true;} finally {lock.unlock();}
}2.6 覆盖数据清空集合
覆盖数据就是set方法可以将指定位置的数据替换
// 覆盖数据
public E set(int index, E element) {// 拿锁加锁final ReentrantLock lock this.lock;lock.lock();try {// 获取原数组Object[] elements getArray();// 获取原数组的原位置数据E oldValue get(elements, index);// 原数据和新数据不一样if (oldValue ! element) {// 拿到原数据的长度复制一份副本。int len elements.length;Object[] newElements Arrays.copyOf(elements, len);// 将element替换掉副本数组中的数据newElements[index] element;// 写回原数组setArray(newElements);} else {// 原数据和新数据一样啥不干把拿出来的数组再写回去setArray(elements);}// 返回原值return oldValue;} finally {// 释放锁lock.unlock();}
}清空就是清空了~~~
public void clear() {// 加锁final ReentrantLock lock this.lock;lock.lock();try {// 扔一个长度为0的数组setArray(new Object[0]);} finally {lock.unlock();}
}2.7 迭代器
用ArrayList时如果想在遍历的过程中去移除或者修改元素必须使用迭代器才可以。
但是CopyOnWriteArrayList这哥们即便用了迭代器也不让做写操作
不让在迭代时做写操作是因为不希望迭代操作时会影响到写操作还有不希望迭代时还需要加锁。
// 获取遍历CopyOnWriteArrayList的iterator。
public IteratorE iterator() {// 其实就是new了一个COWIterator对象并且获取了array指定从0开始遍历return new COWIteratorE(getArray(), 0);
}static final class COWIteratorE implements ListIteratorE {/** 遍历的快照 */private final Object[] snapshot;/** 游标索引~~~ */private int cursor;// 有参构造private COWIterator(Object[] elements, int initialCursor) {cursor initialCursor;snapshot elements;}// 有没有下一个元素基于遍历的索引位置和数组长度查看public boolean hasNext() {return cursor snapshot.length;}// 有没有上一个元素public boolean hasPrevious() {return cursor 0;}// 获取下一个值游标动一下public E next() {// 确保下个位置有数据if (! hasNext())throw new NoSuchElementException();return (E) snapshot[cursor];}// 获取上一个值游标往上移动public E previous() {if (! hasPrevious())throw new NoSuchElementException();return (E) snapshot[--cursor];}// 拿到下一个值的索引返回游标public int nextIndex() {return cursor;}// 拿到上一个值的索引返回游标public int previousIndex() {return cursor-1;}// 写操作全面禁止public void remove() {throw new UnsupportedOperationException();}public void set(E e) {throw new UnsupportedOperationException();}public void add(E e) {throw new UnsupportedOperationException();}// 兼容函数式编程Overridepublic void forEachRemaining(Consumer? super E action) {Objects.requireNonNull(action);Object[] elements snapshot;final int size elements.length;for (int i cursor; i size; i) {SuppressWarnings(unchecked) E e (E) elements[i];action.accept(e);}cursor size;}
}