集合类源码(三)Collection之List(CopyOnWriteArrayList, Stack)
CopyOnWriteArrayList
功能
全名
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
简述
ArrayList的线程安全变体,其中所有的可变操作(添加、修改等)都是通过创建底层数组的新副本来实现的。
方法
// 返回列表里元素的数量
public int size() // 如果此列表不包含任何元素,则返回true。
public boolean isEmpty() // 如果此列表包含至少一个指定的元素,则返回true。
public boolean contains(Object o) // 返回该列表中指定元素第一次出现的索引,如果该列表不包含该元素,则返回-1。
public int indexOf(Object o) // 返回该列表中指定元素第一次出现时的索引(从指定索引向前搜索),如果没有找到该元素,则返回-1。
public int indexOf(E e, int index) // 返回该列表中指定元素的最后一次出现的索引,如果该向量不包含该元素,则返回-1。
public int lastIndexOf(Object o) // // 返回此列表中指定元素的最后一次出现的索引(从指定索引向后搜索),如果没有找到该元素,则返回-1。
public int lastIndexOf(E e, int index) // 返回该列表的浅拷贝。
public Object clone() // 返回一个数组,该数组按适当的顺序(从第一个元素到最后一个元素)包含列表中的所有元素。返回的数组将是“安全的”,因为这个列表不维护对它的引用。(换句话说,这个方法分配一个新数组)。因此,调用者可以自由地修改返回的数组。
public Object[] toArray() // 返回一个数组,该数组按适当的顺序包含列表中的所有元素(从第一个元素到最后一个元素);返回数组的运行时类型是指定数组的运行时类型。
public <T> T[] toArray(T[] a) // 返回列表中指定位置的元素。
public E get(int index) // 将列表中指定位置的元素替换为指定元素。
public E set(int index, E element) // 将指定的元素追加到此列表的末尾。
public boolean add(E e) // 将指定元素插入到列表中的指定位置。将当前位于该位置的元素和任何后续元素向右移动
public void add(int index, E element) // 删除列表中指定位置的元素。将任何后续元素向左移动。返回从列表中删除的元素。
public E remove(int index) // 从该列表中删除指定元素的第一个匹配项
public boolean remove(Object o) // 如果列表中没有此元素,则添加
public boolean addIfAbsent(E e) // 如果此列表包含指定集合的所有元素,则返回true。
public boolean containsAll(Collection<?> c) // 从此列表中移除指定集合中包含的所有元素。因为需要一个内部临时数组,所以在这个类中这是一个特别昂贵的操作。
public boolean removeAll(Collection<?> c) // 仅保留此列表中包含在指定集合中的元素。
public boolean retainAll(Collection<?> c) // 将指定集合中尚未包含在此列表中的所有元素按照指定集合的迭代器返回它们的顺序追加到此列表的末尾。
public int addAllAbsent(Collection<? extends E> c) // 从列表中删除所有元素。该调用返回后,列表将为空。
public void clear() // 将指定集合中的所有元素按照指定集合的迭代器返回它们的顺序追加到此列表的末尾。
public boolean addAll(Collection<? extends E> c) // 从指定位置开始,将指定集合中的所有元素插入此列表。
public boolean addAll(int index, Collection<? extends E> c) // 为可迭代的每个元素执行给定的操作,直到处理完所有元素或操作引发异常。
public void forEach(Consumer<? super E> action) // 删除此集合中满足给定谓词的所有元素。迭代期间或由谓词抛出的错误或运行时异常将传递给调用者。
public boolean removeIf(Predicate<? super E> filter) // 将此列表中的每个元素替换为将操作符应用于该元素的结果。操作符抛出的错误或运行时异常将传递给调用者。
public void replaceAll(UnaryOperator<E> operator) // 根据比较器产生的顺序对这个列表进行排序。
public void sort(Comparator<? super E> c) // 返回此列表的字符串表示形式。字符串表示由列表元素的字符串表示形式组成,其顺序由其迭代器返回,包含在方括号中(“[]”)。
public String toString() // 将指定的对象与此列表进行相等性比较。如果指定的对象是与此对象相同的对象,并且迭代器在指定列表上返回的元素序列与迭代器在此列表上返回的序列相同,则返回true。如果两个序列长度相同,且序列中相同位置对应的元素相等,则认为两个序列是相同的。
public boolean equals(Object o) // 返回此列表的哈希码值。这个实现使用List.hashCode()中的定义。
public int hashCode() // 按适当的顺序对列表中的元素返回一个迭代器。
public Iterator<E> iterator() // 返回该列表中元素的列表迭代器(按适当的顺序)。
public ListIterator<E> listIterator() // 返回此列表中元素的列表迭代器(按适当的顺序),从列表中的指定位置开始。
public ListIterator<E> listIterator(int index) // 在此列表中的元素上创建延迟绑定和快速失败的Spliterator。
public Spliterator<E> spliterator() // 返回该列表中fromIndex(包含)和toIndex(排除)之间部分的视图。返回子列表中的更改将反映在此列表中。
public List<E> subList(int fromIndex, int toIndex)
原理
add
public boolean add(E e) {
// 使用ReentrantLock保证了线程安全
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取当前数组
Object[] elements = getArray();
// 数组长度
int len = elements.length;
// 复制当前数组,新数组长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 设置新数据
newElements[len] = e;
// 将老数组替换成新数组
setArray(newElements);
return true;
} finally {
// 解锁
lock.unlock();
}
}
addIfAbsent
public boolean addIfAbsent(E e) {
// 获取当前数组
Object[] snapshot = getArray();
// 先判断当前数组中是否存在目标对象,存在则返回false,不做添加;否则添加。
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
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
// 对另一个竞争失败的addXXX操作进行优化
// 得到两份数据长度较小的一个
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
// 如果与快照相比对应位置的数据不同,并且待添加对象和当前位置的数据相同,则返回false,停止add操作
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
// (前面循环没有触发条件)查看当前数组中是否存在目标对象,如果有即返回false,停止add操作
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();
}
}
remove
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取当前数组
Object[] elements = getArray();
int len = elements.length;
// 用临时变量保存老对象
E oldValue = get(elements, index);
// 计算要移动的元素
int numMoved = len - index - 1;
if (numMoved == 0)
// 如果没有要移动的,代表删除了最后一个元素,只需要把末尾之前的数据复制到一份新数组即可
setArray(Arrays.copyOf(elements, len - 1));
else {
// 创建一个新数组
Object[] newElements = new Object[len - 1];
// 把删除位置之前的数据复制到新数组
System.arraycopy(elements, 0, newElements, 0, index);
// 把删除位置之后的数据复制到新数组
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
// 将老数组替换成新数组
setArray(newElements);
}
// 返回删除的对象
return oldValue;
} finally {
lock.unlock();
}
}
get
public E get(int index) {
return get(getArray(), index);
}
优缺点
优点:根据数据结构和实现逻辑,这个适合“读多写少”的场景。
缺点:其一、因为它添加和删除都是要复制一份新数组,所以内存占用是个问题;其二、因为读不加锁,如果此时有add或者remove操作未完成,那么在它读的那一刻就决定了它拿到的是老数据。所以它只能保证数据的最终一致性,而不能保证实时一致性。
Stack
功能
全名
public
class Stack<E> extends Vector<E>
简述
Stack类表示对象的后进先出(LIFO)堆栈。它通过5个操作扩展了Vector类,这5个操作允许将一个Vector视为一个堆栈。
Deque接口及其实现提供了一组更完整、更一致的后进先出堆栈操作,应该优先使用这些操作。例如:
Deque stack = new ArrayDeque();
方法
// 压栈操作,将对象压入堆栈顶部。与addElement(item)功能相同
public E push(E item) // 出栈操作,删除此堆栈顶部的对象并返回。
public E pop() // 查看此堆栈顶部的对象,但不将其从堆栈中删除。
public E peek() // 查看堆栈是否为空。
public boolean empty() // 返回最接近堆栈顶部的搜索对象与堆栈顶部的距离;堆栈上最上面的对象被认为与堆栈顶部的距离是1。
public int search(Object o)
原理
push
// push内部调用的是Vector中的同步方法addElement,其中的扩容逻辑在前面已经讲过,这里不再赘述
// 有一点需要注意的是,Stack的push,实际上是把新元素放在了Vector内部存储结构的末尾。对应的pop(removeElementAt(len - 1))和search(int i = lastIndexOf(o))操作,也是从末尾开始的。
// 说白了,Stack将Vector的尾部当成栈顶。
public E push(E item) {
addElement(item); return item;
} public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
pop
public synchronized E pop() {
E obj;
int len = size(); // 先用临时变量存储栈顶元素
obj = peek();
// 然后移除栈顶元素
removeElementAt(len - 1); return obj;
}
public synchronized void removeElementAt(int index) {
// 内部结构修改次数 +1
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
// 将index之后的数据向前复制
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
peek
public synchronized E peek() {
int len = size(); if (len == 0)
throw new EmptyStackException();
// 实际上调用Vector里的elementAt方法
return elementAt(len - 1);
} public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
// 直接返回下标位置的元素
return elementData(index);
}
search
public synchronized int search(Object o) {
// 从栈顶开始,找到目标第一次出现的位置
int i = lastIndexOf(o); if (i >= 0) {
// 返回与栈顶的距离
return size() - i;
}
return -1;
}
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount); if (o == null) {
// 从后向前搜索,找到第一个匹配的即返回
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
// 从后向前搜索,找到第一个匹配的即返回
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
优缺点
优点:实现了栈的数据结构,方便了开发;push和pop操作,都是发生在数组的末尾,没有了元素的移动,提高了效率。
缺点:它是基于Vector的,操作都加了同步代码,效率有所降低,毕竟有的情况下没有线程安全问题。
集合类源码(三)Collection之List(CopyOnWriteArrayList, Stack)的更多相关文章
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
- JDK8集合类源码解析 - HashSet
HashSet 特点:不允许放入重复元素 查看源码,发现HashSet是基于HashMap来实现的,对HashMap做了一次“封装”. private transient HashMap<E,O ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- AQS源码三视-JUC系列
AQS源码三视-JUC系列 前两篇文章介绍了AQS的核心同步机制,使用CHL同步队列实现线程等待和唤醒,一个int值记录资源量.为上层各式各样的同步器实现画好了模版,像已经介绍到的ReentrantL ...
- Java集合类源码解析:ArrayList
目录 前言 源码解析 基本成员变量 添加元素 查询元素 修改元素 删除元素 为什么用 "transient" 修饰数组变量 总结 前言 今天学习一个Java集合类使用最多的类 Ar ...
- Java集合类源码解析:AbstractList
今天学习Java集合类中的一个抽象类,AbstractList. 初识AbstractList AbstractList 是一个抽象类,实现了List<E>接口,是隶属于Java集合框架中 ...
- java集合类源码学习二
我们查看Collection接口的hierarchy时候,可以看到AbstractCollection<E>这样一个抽象类,它实现了Collection接口的部分方法,Collection ...
- Java集合类源码分析
常用类及源码分析 集合类 原理分析 Collection List Vector 扩充容量的方法 ensureCapacityHelper很多方法都加入了synchronized同步语句,来保 ...
- Java集合类源码解析:AbstractMap
目录 引言 源码解析 抽象函数entrySet() 两个集合视图 操作方法 两个子类 参考: 引言 今天学习一个Java集合的一个抽象类 AbstractMap ,AbstractMap 是Map接口 ...
- JDK1.8源码(三)——java.lang.String 类
String 类也是java.lang 包下的一个类,算是日常编码中最常用的一个类了,那么本篇博客就来详细的介绍 String 类. 1.String 类的定义 public final class ...
随机推荐
- 花了近十年的时间,整理出史上最全面Java面试题
1.String 是最基本的数据类型吗? 不是.Java中的基本数据类型只有8个:byte.short.int.long.float.double.char.boolean:除了基本类型(primit ...
- Web Api 模型绑定 二
[https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.2] 1.ApiController属性使模型验证错误 ...
- CSS3制作文字背景图
文字带上渐变色,或者说让文字透出图片.这些效果 CSS 属性也可以完成. 方法一.利用CSS3属性mix-blend-mode:lighten;实现 使用 mix-blend-mode 能够轻易实现, ...
- RabbitMQ启动报unknown exchange type 'x-delayed-message'
RabbitMQ延迟队列插件未安装,导致以下问题: ShutdownSignalException: connection error; protocol method: #method<con ...
- 【原创】Centos 7 升级安装python3.7.4
1.安装必须的软件 #更新源中包列表 yum -y update #先安装扩展源EPEL 才能安装pip 否则会报错 yum -y install epel-release //解决ssl问题,否则报 ...
- mysql update运行超时解决方案
问题描述: 今天update(修改)mysql数据库中一张表时,发现时间很长,而且会失败.报错:Error Code: 1205. Lock wait timeout exceeded; try re ...
- flask上下文管理之threading.local
Flask之上下文管理 知识储备之问题情境: request中的参数: 单进程单线程 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.l ...
- windows下git安装过程
参考廖雪峰博客: https://www.liaoxuefeng.com/wiki/896043488029600/896067074338496 git网站下载程序: https://git-s ...
- modbus_tk模块
modbus_tk模块 通过modbus-RTU 读取地址,调用后返回反馈数值和故障信息. modbus_tk模块安装 pip install pymodbus_tk 下面代码功能:读取地址为0x42 ...
- 09事件传递参数-封装网络请求api get和post合并整合在一起
1==>通过点击事件进行传递参数 <view bindtap="goEdution" data-index="5">西南大学</view ...