ArrayList源码解读

简介

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;
}
数组扩容机制
  1. ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
  1. 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;
}
  1. ensureExplicitCapacity(int minCapacity):判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
	
	// 如果当前数组容量已满
	if (minCapacity - elementData.length > 0)
		// 扩容数组
		grow(minCapacity);
}
  1. 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;
  1. 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 通过实现 writeObjectreadObject 方法自定义了序列化行为,只序列化了实际存储的元素,而不是整个 **<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 接口的实现,使得它可以被用于需要序列化的场景。


Reference

  1. java ArrayList源码分析(深度讲解)-CSDN博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值