List、Set、HashMap作为Java中常用的集合,需要深入认识其原理和特性。
本篇博客介绍常见的关于Java中List集合的面试问题,结合源码分析题目背后的知识点。
关于的Set的博客文章如下:
关于HaseMap的博客文章如下:
其他相关的List的文章合集如下:
手动实现ArrayList & 源码的初步理解分析 & 数组插入数据和删除数据的问题
Java学数据结构(1)——抽象数据类型ADT & 表List、栈Stack和队列Qeue
1.ArrayList如何扩容,1.5倍;
2.ArrayList如何拷贝,深拷贝,浅拷贝;
3.ArrayList的增加或者删除效率低,arraycopy方法;
4.指定长度创建ArrayList对象,避免频繁扩容;
5.线程安全的ArrayList集合:Collections.synchronizedList;
6.LinkedList 和 ArrayList 该如何选择:ArrayList增删效率低,查询效率高;LinkedList 查询效率低,增删效率高
7.Vector 集合和 ArrayList 区别:Vector扩容机制为原始的2倍,线程安全;
ArrayList初始化的时候,若没有给定长度,则默认调用无参构造
此处elelmentData为ArrayList底层数组,后面DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为常量数组初值为空,即长度为0
当执行add方法添加第一个元素时,执行下面的代码
此处涉及到两条代码:
ensureCapacityInternal 方法
calculateCapacity方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果数组是空的
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//返回数组的容量,DEFAULT_CAPACITY=10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
此时集合可以添加默认的10个元素
当添加第11个元素时,ensureExplicitCapacity方法中,minCapacity为11,而原数组长度为10,所以if结构进入grow方法-扩容核心方法
ensureExplicitCapacity方法
grow方法-扩容核心方法
private void grow(int minCapacity) {
// overflow-conscious code
//把旧的长度赋值给oldCapacity
int oldCapacity = elementData.length;
//新的长度就=旧的长度*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//按照新的长度复制出一个新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
oldCapacity赋值为原数组长度,为10 ,newCapacity赋值为原长度1.5倍,即15
调用复制数组方法,将之前的数组复制到新的数组中,并将需要添加的元素加到数组最后一位,完成扩容!
重写clone方法,ArrayList克隆
Java进阶(4)——结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化
浅拷贝:虽然返回一个元素一样的ArrayList,复制的是元素的引用,即其中一个改变了元素,另一个也会跟着改变
两个集合中间存储了同一份元素的引用
例如:
深拷贝:重写clone方法,利用迭代器iterator或遍历集合,重新创建引用对象,逐个添加
例如:
list.clone()
clone.addALl(list);
Collections.copy(clone,list);
效率是很低的,因为ArrayList无论是增加或者删除某个对象,我们都要通过对数组中的元素进行移位来实现。
而这种移位就需要不断的arraycopy,是很耗时间的,所以效率自然也很低。
增加元素时
删除元素时
指定长度创建ArrayList对象,避免频繁扩容
List<Object> datas = Collections.synchronizedList(new ArrayList<>());
从源码可以看到集合操作都加了synchronized 关键字,保证了在同一时刻,数组和链表只会被一个线程所修改。
1.ArrayList如何扩容,1.5倍;
2.ArrayList如何拷贝,深拷贝,浅拷贝;
3.ArrayList的增加或者删除效率低,arraycopy方法;
4.指定长度创建ArrayList对象,避免频繁扩容;
5.线程安全的ArrayList集合:Collections.synchronizedList;
6.LinkedList 和 ArrayList 该如何选择:ArrayList增删效率低,查询效率高;LinkedList 查询效率低,增删效率高
7.Vector 集合和 ArrayList 区别:Vector扩容机制为原始的2倍,线程安全;