业精于勤荒于嬉,行成于思毁于随;
在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,开始思考…
今天一起来聊一聊 List集合,看到这里,笔者懂,大家莫慌,先来宝图镇楼 ~
这年轻人,不讲武德,
来,骗!
来,偷袭!
我69岁的老同志。
这好吗?这不好。
我劝这位年轻人,耗子尾汁。
咳咳… 相信大家满脑子的ArrayList已被保国爷爷经典的画面以及台词冲淡了,那么,目的已达到,那我们言归正传,对于屏幕前帅气的猿友们来说,ArrayList,LinkedList,Vector,CopyOnWriteArrayList… 张口就来,闭眼能写,但是呢,我相信大部分的猿友们并没有刨根问底真正去看过其源码,此时,笔者帅气的脸庞似有似无洋溢起一抹微笑,毕竟是查看过源码的猿,就是那么的不讲武德,吃我一记闪电五连鞭,话不多说,来吧,展示…
// CopyOnWriteArrayList底层为数组 transient关键字:当前数组不能被序列化
private transient volatile Object[] array;
/**
* 入口
*/
public CopyOnWriteArrayList() {
// 数组初始化
setArray(new Object[0]);
}
// 数组初始化
final void setArray(Object[] a) {
// 初始化长度为0的数组,array = new Object[0];
array = a;
}
从源码中可以看出,构造只为底层数组进行初始化,默认长度为0的空数组;
// lock锁
final transient ReentrantLock lock = new ReentrantLock();
/**
* 入口 - ReentrantLock锁:保证线程安全
*/
public boolean add(E e) {
// 设置锁
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取源数组
Object[] elements = getArray();
// 获取源数组长度
int len = elements.length;
// copy (源数组长度+1)的新数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 添加元素
newElements[len] = e;
// 赋值给源数组
setArray(newElements);
return true;
} finally {
// 释放锁
lock.unlock();
}
}
// 获取源数组
final Object[] getArray() {
return array;
}
从源码中可以看出,添加元素时每次都会先复制一个(源数组长度+1)的新数组,添加元素后再赋值给源数组;
知识点:
每次添加元素,扩容+1;
ReentrantLock锁,保证add()方法线程安全;
/**
* 入口 - 线程不安全
*/
public E get(int index) {
// 获取元素
return get(getArray(), index);
}
// 通过下标获取元素
private E get(Object[] a, int index) {
return (E) a[index];
}
从源码中可以看出,获取元素时就是获取数组元素,通过下标直接获取即可;
知识点:
无ReentrantLock锁,get()方法线程不安全;
/**
* 入口 - ReentrantLock锁:保证线程安全
*/
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;
// 0:删除的元素为数组最后一个
if (numMoved == 0)
// 直接复制(源数组长度-1)的新数组
setArray(Arrays.copyOf(elements, len - 1));
else {
/**
* public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
* 说明此方法参数作用:
* src:源数组
* srcPos:源数组要复制的起始位置
* dest:目的数组
* destPos:目的数组放置的起始位置
* length:复制的长度
*/
// 创建(源数组长度-1)的新数组
Object[] newElements = new Object[len - 1];
// 看过前几篇文章的猿友应该知道,这里的源数组和目标数组是两个数组,所以这里用的是复制数组,而不是移动覆盖
// 第一次copy:将数组中删除元素前的所有元素复制到目标数组
System.arraycopy(elements, 0, newElements, 0, index);
// 第二次copy:将数组中删除元素后的所有元素复制到目标数组
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
// 将目标数组赋值给源数组
setArray(newElements);
}
// 返回删除元素值
return oldValue;
} finally {
// 释放锁
lock.unlock();
}
}
从源码中可以看出,删除元素实则为数组复制copy的过程;
注意:
相信对于有经验的开发猿友来说,已经看出来了,CopyOnWriteArrayList删除元素时与ArrayList删除元素时不同,ArrayList删除元素时,源数组与目标数组是同一数组,所以用的是移动覆盖的过程,而CopyOnWriteArrayList删除元素时,参考上述代码我们发现,源数组与目标数组非同一数组,故用的是copy复制新数组的方式,并且复制了2次;
- 底层为数组;
- 构造初始化,数组长度为0的空数组,集合size为0,数组length为0;
每次新增元素,扩容为(源数组长度+1);- 通过下标去获取元素,故查询效率高,增删效率低;
- (新增/删除)元素方法-线程安全,获取元素方法-线程不安全;
- 无modCount;
不讲武德,一起聊聊List集合之ArrayList
不讲武德,一起聊聊List集合之LinkedList
不讲武德,一起聊聊List集合之Vector
大家好,我是猿医生:
在码农的大道上,唯有自己强才是真正的强者,求人不如求己,静下心来,扫码一起学习吧…