1、谈谈Arraylist
1. ArrayList 底层数据结构是数组,数组的特点就是可以快速随机访问,直接根据下标定位,缺点是插入和删除速度比较慢,需要移动元素。
2. ArrayList 每次扩容之后的大小为之前的 1.5 倍。默认初始容量大小为 10。ArrayList只不过是对数组的包装,因为数组在内存中分配时必须指定长度,且一旦分配好后便无法再增加长度,即不可能在原数组后面再接上一段的。ArrayList之所以可以一直往里添加,是因为它内部做了处理。当底层数组填满后,它会再分配一个更大的新的数组,把原数组里的元素拷贝过来,然后把原数组抛弃掉。使用新的数组作为底层数组来继续存储。
Arraylist不安全代码:
public class ArraylistDemo {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
for (int i = 0; i< 30; i++) {
final int temp=i;
new Thread(()->{
arrayList.add(temp);
System.out.println(arrayList);
},String.valueOf(i)).start();
}
}
}
结果:
2、Arraylist不安全的解决方案,至少三个
1、Vector。 Vector arrayList = new Vector<>();
2、Collections.synchronizedList。
List arrayList = Collections.synchronizedList(new ArrayList());
3、CopyOnWriteArrayList写时复制。
原理:往一个容器中添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行copy,复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加新元素,添加元素之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处是可以对CopyOnWrite容器进行并发的读,而不是需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
多线程及并发场景下我们使用的CopyOnWriteArrayList。
结合CopyOnWrite中add操作的源码深入理解一下它的原理:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
源码中使用了lock锁机制。并在复制时使用了Arrays.copyOf方法,然后讲新值插入到复制之后的容器尾部。