ArrayList是一种基于定长数组实现的变长集合类,线程不安全,易查询,难增删
源码分析
ArrayList全局属性
定义了初始容量10
使用默认构造方法初始化出来的容量是 10(1.7 之后都是延迟初始化,即第一次调用 add 方法添加元素的时候才将elementData 容量初始化为 10)。
private static final int DEFAULT_CAPACITY = 10;
定义了2个大小为0的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
声明了一个数组
transient修饰不被序列化
transient Object[] elementData;
定义了size变量
标识数组大小
private int size;
ArrayList构造方法
ArrayList无参构造
将之前声明创建的大小为0的数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋给elementData数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList有参构造
传入一个int类型的变量,指定使用ArrayList的容量。如果传入的参数initialCapacity大于0,则新建一个initialCapacity大小的数组。传入参数值等于0的话,将之前创建的EMPTY_ELEMENTDATA空数组给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);
}
}
ArrayList方法
add方法
add方法中调用了ensureCapacityInternal方法 并将size+1作为参数传入
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal方法
首先调用了calculateCapacity方法作为参数传入
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity方法
判断当前数组是否为之前定义的空数组,
若为空则返回默认容量10和传入参数的最大值
若不为空则直接返回传入的数值,然后返回ensureExplicitCapacity方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ensureExplicitCapacity方法
modCount是ArrayList继承抽象父类AbstractList中的一条属性,初始为0
protected transient int modCount = 0;
首先进行判断,如果传入的数组长度减去数组原先的长度大于0,我们则调用grow方法(扩容方法),并将新长度传入进去。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow方法
这段代码就是扩容的核心,首先看前两行代码定义了旧长度和新长度
新长度=旧长度+旧长度>>1(右移运算) 等同于 新长度=旧长度+0.5旧长度=1.5旧长度,所以ArrayList底层数组每次扩容时都是先将原数组长度的1.5倍与传入的数组长度进行比较。
接下来进入if判断
第一个if,如果新长度小于传入的数组长度,那么就更新newCapacity(新长度)的值为minCapacity(传入长度)
第二个if,如果新数长度大于最大的数组长度,会进入hugeCapacity方法再次进行判断,返回Integer的最大长度,或者Integer的最大长度-8的长度。
最后通过Arrays.copyOf方法进行数组扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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);
}
hugeCapacity方法
Integer最大值
@Native public static final int MAX_VALUE = 0x7fffffff;
arraylist数组长度最大值
为什么是Integer最大长度-8而不是-7或者-6呢?
数组类型是由jvm从元素类型合成出来的;在jvm中获取数组的长度是用arraylength这个专门的字节码指令的,在数组的对象头里有一个_length字段,记录数组长度,只需要去读_length字段,所以ArrayList中定义的最大长度为Integer最大值减8,这个8就是就是存了数组_length字段。Integer是int类型的包装类,而int类型在内存中占8bit
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
remove方法
删除是先判断删除的数组坐标是否越界,然后使用System.arraycopy进行数组复制,然后让旧的elementData最后一位置为null进行GC垃圾清理
public E remove(int index) {
rangeCheck(index);//是判断数组坐标是否越界抛出异常的方法
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
get方法
get方法较为简单,判断是否越界后直接返回数组
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
set方法
set方法也很简单,判断是否越界后,取旧值,再赋新值,最后返回旧值
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
本文深入剖析了ArrayList的内部实现机制,包括其初始化过程、数组扩容原理、基本操作方法如add、remove、get和set等,并解释了ArrayList在不同场景下的性能表现。
457

被折叠的 条评论
为什么被折叠?



