1. 单列集合 List
1.1 Vector 和 ArrayList 以及 LinkedList 区别和联系, 以及分别的应用场景
线程安全:
Vector: 如果创建 Vector 时没有指定容量, 则默认容量为 10, 底层基于数组实现, 线程是安全的, 底层采用 synchronized 同步方法进行加锁
ArrayList: 底层基于数组, 线程不安全, 查询和修改效率高, 但是增加和删除效率低
LinkedList: 底层双向链表结构, 线程不安全, 查询和修改效率低, 但是增加和删除效率高
使用场景:
1.Vector 很少用
2. 如果需要大量的添加和删除则可以选择 LinkedList
3. 如果需要大量的查询和修改则可以选择 ArrayList
1.2 如果要保证 ArraList 线程安全, 有几种方式?
1.2.1 自己重写一个 ArrayList 集合类, 根据业务一般来说, add/set/remove 加锁
1.2.2 利用 List<Object> list = Collections.synchronizedList(new ArrayList<>()); // 采用 synchronized 加锁
1.2.3 new CopyOnWriteArrayList<>().add(""); // 采用 ReentrantLock 加锁
1.3 了解 CopyOnWriteArrayList 底层?,CopyOnWriteArrayList 与 Collections.synchronizedList 有什么区别
1.3.1 CopyOnWriteArrayList 底层实现:
CopyOnWriteArrayList 在执行修改操作的时候, 会复制一份新的数组数据, 代价昂贵, 修改过后将原来的集合指向到新的集合完成操作
使用 ReentrantLock 保证多线程环境下的集合安全
- public boolean add(E e) {
- final ReentrantLock lock = this.lock; // 获取了一把锁
- lock.lock(); // 加锁
- try {
- Object[] elements = getArray(); // 获取当前数组数据, 给 elements
- int len = elements.length; // 记录当前数组的长度
- Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制一个新的数组
- newElements[len] = e; // 将数据填入到新数组当中
- setArray(newElements); // 将当前 array 指针指向到新的数据
- return true;
- } finally {
- lock.unlock(); // 释放锁
- }
- }
CopyOnWriteArrayList 应用场景: 适用于读取操作远大于写操作场景 (底层 get 读取时没有加锁, 直接获取)
1.3.2 Collections.synchronizedList 几乎底层方法都加上了 synchronized 的锁
场景: 写操作的性能比 CopyOnWriteArrayList 要好, 但是读取的性能不如 CopyOnWriteArrayList
1.4 CopyOnWriteArrayList 设计思想是怎么样的, 有什么缺点?
设计思想: 读写分离, 最终一致
缺点: 内存占用, 由于写时复制, 内存中就会出现两个对象占用空间, 如果对象大则容易发生 YoungGC 和 FullGC
1.5 说一下 ArrayList 扩容机制是怎么样的
1.7 以及之前版本 JDK, 首先从默认大小来讲, 默认为 10
1.8 ArrayList 集合大小如果创建时没有指定, 则默认为 0, 若已经指定集合大小, 则初始值为指
当第一次添加数据的时候, 集合大小扩容为 10, 第二次及其后续每次按照 int oldCapacity = elementData.length; newCapacity = oldCapacity+(oldCapacity>>1)
来源: http://www.bubuko.com/infodetail-3461287.html