版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/onwl007/article/details/82315907
前言
最近在面试中,面试的几家公司几乎都问到了是否看过 Java 的源码,我只能回答没有看过。在之前的学习中,确实忽略了看源码这一重要的学习手段。
最近准备看一看面试中常问的一些关于 Java 源码的。
ArrayList
ArrayList 和 LinkedList 是面试中几乎必问的,所以今天先分析一下 ArrayList 的源码。
原理
ArrayList 是我们使用最多的集合,内部是动态数组实现的,其容量是自动增长的。
本身是非线程安全的,没有同步方面的开销,只能用在单线程环境下,多线程环境下,可以考虑 Java 并发包下提供的 CopyOnWriteArrayList 类。
源码分析
//默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
//空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//使用默认初始容量的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据的数组
transient Object[] elementData;
//实际数据大小
private int size;
//==================三种构造函数=======================
//指定初始容量的构造函数
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//如果指定容量大于0,就初始化这么大的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果等于0,先指向空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//不带参数的构造函数,指向默认初始容量的空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带有集合参数的构造函数
public ArrayList(Collection<? extends E> c) {
//直接将集合的数组传给 elementData
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray 可能不会返回一个数组
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//为空就指向一个空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
//将当前容量值设为实际元素个数
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
//确保当前容量
public void ensureCapacity(int minCapacity) {
//如果不是默认的初始容量则为0,否则就设置为默认的初始容量
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
//如果当前元素大于容量则扩容
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//扩容
private void ensureCapacityInternal(int minCapacity) {
//初始化
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//更新容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果最小容量大于元素个数,放置溢出
if (minCapacity - elementData.length > 0)
//增长
grow(minCapacity);
}
//数组最大容量是整数最大容量减8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//增长容量
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩大的新容量是旧容量的1.5倍
//右移1位,相当于除以2
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);
}
private static int hugeCapacity(int minCapacity) {
//如果大于最大数组容量,则设为整数最大容量,否则设为数组最大容量
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}