1.说下ArrayList、LinkedList和Vector区别?分别使用场景
ArrayList:底层数组实现,线程不安全,查询和修改速度快,但是增加和删除慢
LinkedList:底层是双向链表,线程不安全,查询和修改速度慢,但是增加和删除快
Vector:底层是数组实现,线程安全,操作的时候使用synchronized进行加锁
使用场景
Vector已经很少用了
增加和删除场景多使用LinkedList
查询和修改多使用ArrayList
2.如果需要保证线程安全,ArrayList应该怎么做
方式一:自己写个包装类,根据业务,一般是add/update/remove加锁
方式二:Collections.synchronizedList(new ArrayList<>());使用synchronized加锁
方式三:new CopyOnWriteArrayList();使用ReentrantLock加锁
3.CopyOnWriteArrayList和Collections.synchronizedList实现线程安全区别和使用场景
CopyOnWriteArrayList:执行修改操作时,会拷贝一份新的数组进行操作(add、set、remove等),代价十分昂贵,在执行完修改后将原来的集合指向新的集合来完成修改操作,源码里面用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组
场景:读高性能。适用读操作远大于写操作的场景(读的时候不需要加锁,直接获取,删除和增加是需要加锁的)
Collections.synchronizedList:线程安全的,因为它几乎在每个方法中都使用了synchronized同步锁
场景:写操作性能比CopyOnWriteArrayList好,读操作性能不如CopyOnWriteArrayList
4.CopyOnWriteArrayList的设计思想,有什么缺点
设计思想:读写分离+最终一致
缺点:内存占用问题,写时复制机制,内存里会同时存储两个对象的内存,旧的对象和新写入的对象,如果对象大则容易发生Yong GC和Full GC
5.说下ArrayList的扩容机制
注意:JDK7之前ArrayList默认大小是10,JDK7之后默认大小0
未指定集合容量,默认是0,若已经指定大小则集合大小为指定的;
当集合第一次添加元素的时候,集合大小扩容为10
ArrayList的元素个数大于其容量,扩容大小=原始大小+原始大小/2
6.编写简单的ArrayList
import java.io.Serializable;
public class MyArrayList implements Serializable {
// 第一次扩容的容量
private static final int DEFAULT_CAPACITY = 10;
// 初始化空的List
private static final Object[] EMPTY_ELEMENT_DATA = {};
// 实际存储的元素
transient Object[] elementData;
// 实际集合的大小,0开始
private int size;
public MyArrayList() {
this.elementData = EMPTY_ELEMENT_DATA;
}
public MyArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENT_DATA;
} else {
throw new IllegalArgumentException("参数异常");
}
}
/**
* 添加元素
*
* @param e
* @return boolean
* @author Gen
* @date 2023/3/14
*/
public boolean add(Object e) {
// 判断容量是否需要扩容
grow(size + 1);
// 数组末尾插入元素
elementData[size++] = e;
return true;
}
/**
* 扩容
*
* @param minCapacity 所需容量
* @return void
* @author Gen
* @date 2023/3/14
*/
private void grow(int minCapacity) {
// 初次扩容,使用默认的容量
if (elementData == EMPTY_ELEMENT_DATA) {
minCapacity = Math.max(minCapacity, DEFAULT_CAPACITY);
}
// 所需容量大于数组长度则要扩容
if (minCapacity > elementData.length) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量<最小容量,则新容量=最小容量
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
// 数组拷贝
Object[] objects = new Object[newCapacity];
System.arraycopy(elementData, 0, objects, 0, elementData.length);
elementData = objects;
}
}
/**
* 通过索引获取对象
*
* @param index
* @return java.lang.Object
* @author Gen
* @date 2023/3/14
*/
public Object get(int index) {
rangeCheck(index);
return elementData[index];
}
/**
* 检查下标传参
*
* @param index
* @return void
* @author Gen
* @date 2023/3/14
*/
private void rangeCheck(int index) {
if (index >= size || size < 0) {
throw new IndexOutOfBoundsException("数组下标越界");
}
}
/**
* 判断对象所在下标位置
*
* @param o
* @return int
* @author Gen
* @date 2023/3/14
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++) {
if (elementData[i] == null) {
return i;
}
}
} else {
for (int i = 0; i < size; i++) {
if (o.equals(elementData[i])) {
return i;
}
}
}
return -1;
}
/**
* 根据下标修改元素,并返回旧的元素
*
* @param index
* @param obj
* @return java.lang.Object
* @author Gen
* @date 2023/3/14
*/
public Object set(int index, Object obj) {
rangeCheck(index);
Object oldValue = elementData[index];
elementData[index] = obj;
return oldValue;
}
/**
* 根据下标移除元素,并返回旧的元素
*
* @param index
* @return java.lang.Object
* @author Gen
* @date 2023/3/14
*/
public Object remove(int index) {
rangeCheck(index);
Object oldValue = elementData[index];
// 计算要移动的元素个数
int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
// 将多出的位置置空,让垃圾收集器回收;如果不为空,将会保存一个引用,可能造成内存泄露
elementData[--size] = null;
return oldValue;
}
/**
* 返回数组大小
*
* @param
* @return int
* @author Gen
* @date 2023/3/14
*/
public int size() {
return this.size;
}
}