在前面的文章【Java集合框架总结】https://blog.csdn.net/moni_mm/article/details/80065576中对集合框架进行概要分析之后,在此结合JDK源码对ArrayList
进行深入分析。
提出并解决问题如下:
问题1:elementData
非私有化以简化嵌套类的访问,但嵌套类可以透明地访问外围类的所有成员,如何理解这里的简化?
详情见https://blog.csdn.net/moni_mm/article/details/80723880
问题2: transient
表示不可被序列化,为何要这样?
问题3: new ArrayList()和new ArrayList(0)有什么区别,为何要这样?
问题4:最大容量为什么是Integer.MAX_VALUE - 8
?
问题5:System.arraycopy
和Arrays.copyOf
有何区别?
ArrayList
- 继承自
AbstractList
- 实现
List
、RandomAccess
、Cloneable
、Serializable
接口 - 非线程安全
- 基于数组,默认容量10,扩容为1.5倍
重要对象
默认初始容量:10
private static final int DEFAULT_CAPACITY = 10;
共享的空数组实例,用来初始化空的ArrayList
实例
private static final Object[] EMPTY_ELEMENTDATA = {};
当第一个元素被添加时,确定如何扩容
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
存储数组元素的缓冲区,在第一次添加元素时,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
会被扩展成10
transient Object[] elementData; // non-private to simplify nested class access
问题1:非私有化以简化嵌套类的访问,如何理解?
内部类:SubList
的get
方法使用了ArrayList
的elementData
因为它隐式地保存了外部类的引用
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
嵌套类:ArrayListSpliterator
,静态类,不能直接访问外部的非静态elementData
,所以持有了外部类的list
实例,可以访问list的任意成员,那为什么不声明为private
呢?
编译期间自动会为内部类生成access&XX方法
提高JVM底层访问效率
问题2: transient
表示不可被序列化,为何要这样?
transient
告诉JVM自己不可被序列化,并且ArrayList
自己重写了readObject
和writeObject
方法,只序列化了实际存储的那些元素,而不是整个数组,避免浪费
,
真实大小
private int size;
最大容量
为整数最大值-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造方法
无参
- 用
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
初始化 - 第一次添加元素时 以默认容量10 构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
有参
- 以给定的容量new一个Object数组初始化
elementData
- 为负抛异常
- 为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);
}
}
问题3: new ArrayList()和new ArrayList(0)有什么区别,为什么要这样?
- 内部都是以空数组初始化,无本质区别
- new ArrayList()以
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
初始化 - new ArrayList(0)以
EMPTY_ELEMENTDATA
初始化
重要方法
添加元素add
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
添加元素时,首先要确保内部容量安全,再向末端添加元素,然后size++
,注意modCount
也要自增,这个是用来标识修改次数。
如何确保容量安全呢?
minCapacity
表示所需最小容量,值为size+1
- 计算容量,返回所需容量:如果
elementData
为空数组,返回10 - 确定明确容量:如果所需容量大于当前数组长度,进行被动扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
扩容
主动扩容:
ArrayList
向外提供了主动扩容方法,可减少扩容次数,提高程序效率
- 如果是空数组,扩容为10
- 不为空数组就调用
grow
方法扩容至所需容量
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
被动扩容:
- 扩容至当前容量的1.5倍,用
newCapacity
表示 - 扩容后仍不满足,直接扩容成
minCapacity
- 扩容后
newCapacity
为负,表示溢出,抛出异常 - 扩容后
newCapacity
超过最大容量Integer.MAX_VALUE-8
,就扩容成Integer.MAX_VALUE
;否则扩容成Integer.MAX_VALUE-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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
问题4:最大容量为什么是Integer.MAX_VALUE - 8
?
第一种解释:有些虚拟机在数组中保留了一些头信息。避免内存溢出
第二种解释:用来存放数组对象的元数据。
另一个add函数
使用System.arraycopy
进行复制
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos,int length);
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
问题5:System.arraycopy
和Arrays.copyOf
有何区别?
Arrays.copyOf
最终调用了
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));