1.对比 Vector、ArrayList、LinkedList 有何区别?
Vector 是 Java 早期提供的线程安全的动态数组,如果不需要线程安全,并不建议选择,毕竟同步是有额外开销的。Vector 内部是使用对象数组来保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并拷贝原有数组数据。
ArrayList 是应用更加广泛的动态数组实现,它本身不是线程安全的,所以性能要好很多。与 Vector 近似,ArrayList 也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector 在扩容时会提高 1 倍,而 ArrayList 则是增加 50%。
LinkedList 顾名思义是 Java 提供的双向链表,所以它不需要像上面两种那样调整容量,它也不是线程安全的。
ArrayList:底层是数组实现,线程不安全,查询和修改非常快,但是增加和删除慢。
LinkedList: 底层是双向链表,线程不安全,查询和修改速度慢,但是增加和删除速度快。
Vector: 底层是数组实现,线程安全的,操作的时候使用synchronized进行加锁。
2.如果需要保证线程安全,ArrayList应该怎么做?
//方式一:Collections.synchronizedList(new ArrayList<>()); 使用synchronized加锁。
List<Object> objects = Collections.synchronizedList(new ArrayList<>());
//方式二:CopyOnWriteArrayList<>() 使⽤ReentrantLock加锁。
List<String> list = new CopyOnWriteArrayList(new ArrayList<String>());
3.CopyOnWriteArrayList和Collections.synchronizedList实现线程安全有什么区别, 使用场景是怎样的?
CopyOnWriteArrayList:执行修改操作时,会拷贝⼀份新的数组进行操作(add、set、remove等),代价十分昂贵,在执行完修改后将原来集合指向新的集合来完成修改操作,源码里面用ReentrantLock可重入锁来保证不会有多个线程同时拷贝⼀份数组。
场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和增加是需要加锁的, 读多写少)
CopyOnWriteArrayList的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();
}
}
Collections.synchronizedList:线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步锁。
场景:写操作性能比CopyOnWriteArrayList好,读操作性能并不如CopyOnWriteArrayList。
Collections.synchronizedList部分源码
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
}
public boolean containsAll(Collection<?> coll) {
synchronized (mutex) {return c.containsAll(coll);}
}
public boolean addAll(Collection<? extends E> coll) {
synchronized (mutex) {return c.addAll(coll);}
}
4.CopyOnWriteArrayList的设计思想是怎样的,有什么缺点?
答案:设计思想:读写分离+最终⼀致
缺点:内存占用问题,写时复制机制,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,
如果对象⼤则容易发生Yong GC和Full GC。
5.ArrayList的扩容机制是怎样的?
JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0
5.1.未指定集合容量,默认是0,若已经指定大小则集合大小为指定参数;
ArrayList源码四个属性:
/**
* 默认初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空数组.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默认容量的空数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存储数据的数组
*/
transient Object[] elementData;
构造方法:
//指定容量的构造方法,初始化长度为指定容量
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 未指定容量构造方法,初始化长度为0.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
5.2.当集合第一次添加元素的时候,集合大小扩容为10
ArrayList的add()源码解析:
/**
*添加一个指定元素到数组尾部。
*1.调用ensureCapacityInternal(size + 1)方法保证内部容量。
*2.将指定的数据添加到数组尾部。
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
*保证内部的容量,传入参数为add()方法里面传的size+1。
*1.调用calculateCapacity(elementData, minCapacity)方法,计算容量
*2.调用ensureExplicitCapacity()方法。初次添加,参数为10;
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
*计算容量,传入参数为存储数据的数组,当前数组长度+1。
*初次添加元素,返回默认容量10
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
*计算确定的容量:
*初次添加,参数为10,10-数组长度(0)>0,进入grow(10)方法。
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
*初次添加元素,参数为10。
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//新的容量=oldCapacity +oldCapacity /2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//初次添加,newCapacity=0,minCapacity =10。
if (newCapacity - minCapacity < 0)
//将新的容量,赋值为10.
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷贝数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
5.3.ArrayList的元素个数大于其容量,扩容的大小= 原始大小+原始大小/2。
假设当前数组长度为10,需要添加第11个元素,源码分析如下:
/**
*第一次扩容,此时数组长度为10,调用add()方法,添加第11个元素。
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
*保证内部的容量,传入参数为add()方法里面传的size+1。
*1.调用calculateCapacity(elementData, minCapacity)方法,计算容量
*2.调用ensureExplicitCapacity()方法。参数为11;
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
*计算容量,传入参数为存储数据的数组,当前数组长度+1。
*返回minCapacity,minCapacity=11
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
*计算确定的容量:
*minCapacity = 11,
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 11-10>0
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
*minCapacity=11,
*/
private void grow(int minCapacity) {
// oldCapacity = 10
int oldCapacity = elementData.length;
//新的容量=oldCapacity +oldCapacity /2=15
int newCapacity = oldCapacity + (oldCapacity >> 1);
//newCapacity=15,minCapacity =11。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 拷贝数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
网友评论