这里写目录标题
简介
ArrayList 是对 List
接口的一种具体实现,属于单列集合类型。其内部数据结构基于动态数组实现,允许在运行时根据需要自动调整容量,从而实现了对元素的高效存储与访问。
ArrayList 类继承自 AbstractList 抽象类,并且实现了 List 接口以及标记接口 Serializable。通过实现 Serializable 接口,ArrayList 集合的实例能够支持序列化过程,从而允许对象的状态被转换成可以存储或传输的形式,用于网络传输或保存到文件等。
源码解读
基本变量
// ArrayList底层结构是数组,transient 关键字修饰的元素不能被序列化。
transient Object[] elementData;
// 默认的数组长度
private static final int DEFAULT_CAPACITY = 10;
// 空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 记录数组的大小
private int size;
构造函数
ArrayList 集合主要有以下三种构造函数:
ArrayList()
空参构造
// 默认创建一个空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList(int initialCapacity)
初始化指定数组大小
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
}
// 如果容量=0,就创建一个空数组。
else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList(Collection<? extends E> c)
将 Collection 的子类集合元素复制到的 ArrayList。
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
// 传入集合的类型是ArrayList
if (c.getClass() == ArrayList.class) {
elementData = a;
}
// 传入集合不是ArrayList类型,九江
else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
}
// 传入的集合为空,就创建一个空数组
else {
elementData = EMPTY_ELEMENTDATA;
}
}
扩容机制
add()方法源码解读
public boolean add(E e) {
// 确保数组容量足够(当前数组容量 + 1)
ensureCapacityInternal(size + 1); // Increments modCount!!
// 添加元素
elementData[size++] = e;
return true;
}
数组扩容机制
- ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
- calculateCapacity(elementData, minCapacity):计算当前元素长度(包括新元素)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 当前数组为空
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 数组容量只为1
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
- ensureExplicitCapacity(int minCapacity):判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果当前数组容量已满
if (minCapacity - elementData.length > 0)
// 扩容数组
grow(minCapacity);
}
- grow(minCapacity):数组扩容
private void grow(int minCapacity) {
// 原来数组的容量
int oldCapacity = elementData.length;
// 扩容后数组的容量 = oldCapacity + oldCapacity / 2,即扩大1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果oldCapacity = 1,那么 newCapacity = 1,
// 此时minCapacity = 2,就无法存储,所以newCapacity = minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// newCapacity 超过了最大数组容量,则调用hugeCapacity方法来决定合适的容量。
// 这个方法可能会抛出OutOfMemoryError异常,或者返回一个合适的更大容量。
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);
}
ArrayList
的最大容量是Integer.MAX_VALUE - 8
。这是因为在某些虚拟机中,数组对象需要额外的空间来存储数组对象的元数据,例如数组的长度等信息。因此,ArrayList
的内部实现会保留这部分空间,以避免因超出Java数组最大限制而导致的内存溢出错误。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- hugeCapcacity(int minCapacity):扩容数组最大容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果超过了数组最大容量,就将数组容量扩充为 Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
问题
Integer.MAX_VALUE 的容量是多少❓
1. `Integer.MAX_VALUE - 8`,即`2^31 - 1 - 8`,或者`2147483647 - 8`,结果是`2147483647`
既然数据被存储到elementData 数组并且不能被序列化,那为什么 ArrayList 还要实现 Serializable 接口呢❓
因为在 ArrayList
的设计中,elementData
通常会比实际存储的元素数量要大,以减少数组扩容操作的频率。如果直接序列化整个 elementData
,那么会序列化很多不必要的、未使用的空间,这是低效的。
所以ArrayList
通过实现 writeObject
和 readObject
方法自定义了序列化行为,只序列化了实际存储的元素,而不是整个 **<u>elementData</u>**
数组。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// like clone(), allocate array based upon size not capacity
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
elementData = new Object[size];
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
elementData[i] = s.readObject();
}
} else {
elementData = EMPTY_ELEMENTDATA;
}
}
通过这种方式,ArrayList
可以高效地序列化,并且仍然保持 Serializable
接口的实现,使得它可以被用于需要序列化的场景。