小学生也能看懂的ArrayList底层原理

阅读指引:本文包含源码,如果不想阅读源码,建议跳过 “ArrayList 的主要方法” 中的源码分析部分,直接看每一部分的小总结。

简单介绍

ArrayList是 Java 集合框架中比较常用的数据结构,底层基于数组实现,能够实现容量的动态变化。
我们之所以使用它,与它能够自由添加、删除元素有关,使用者不用考虑数组的容量、遍历等问题,可以说是一个功能强大、使用简单的数组。

那么ArrayList里面到底是怎么实现的呢?

ArrayList 的成员变量

了解一个类,我们需要先了解它有哪些变量,通过这些成员变量我们能够大致了解它的实现原理。

ArrayList 的主要变量:

private static final int DEFAULT_CAPACITY = 10;		//数组初始容量
transient Object[] elementData;						//存放数组
private int size;									//实际元素个数,指已经使用的空间数

除此之外,ArrayList 的父类也有一个重要的变量:

protected transient int modCount = 0;				//记录对 List 操作的次数

看到这里,我们不着急看具体方法,让我来猜想一下 ArrayList 主要方法的实现:

  • add(E e):在数组末尾添加元素。若数组为空,则初始化数组,容量为10;若数组已满,则扩容为原来的1.5倍。
  • get(int index):返回指定位置的元素。若index越界,报错。
  • remove(Object o):删除第一次出现对象为 o 的元素。若 o 为 null,查找并删除值为 null 的元素;若 o 不为 null,查找并删除 o 。
  • size():返回ArrayList 中已有元素数量。返回 size。

----------------------------------------------------下面是源码,慎入----------------------------------------------------------

ArrayList 的主要方法

接下来将从源码入手,分析 ArrayList 的主要方法是怎么实现的,可以先深吸一口气,我也将用比较简单的话来描述实现的过程。

1.add方法

有多种不同参数的 add() 方法,我们分析一下比较常用的add(E e):

注:代码中的中文注释是笔者添加的
public boolean add(E e) {
	// 检查内部容量是否充足,如果不足则扩容
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	// 将元素存入数组
	elementData[size++] = e;
	return true;
}

private void ensureCapacityInternal(int minCapacity) {
	// 如果初始数组为默认空数组,minCapacity = 10
	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
	}
	// 检查是否需要扩容
	ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
	// 添加新元素后数组长度 大于 数组当前容量
	if (minCapacity - elementData.length > 0)
		// 数组扩容,扩展为原来的 1.5 倍
		grow(minCapacity);
}

private void grow(int minCapacity) {
	int oldCapacity = elementData.length;
	// 相当于 newCapacity = oldCapacity + oldCapacity / 2
	int newCapacity = oldCapacity + (oldCapacity >> 1);
	if (newCapacity - minCapacity < 0)
		newCapacity = minCapacity;
	if (newCapacity - MAX_ARRAY_SIZE > 0)
		newCapacity = hugeCapacity(minCapacity);
	elementData = Arrays.copyOf(elementData, newCapacity);
}

总结
调用 add(E e) 方法,首先判断 elementData 是否为空数组,如果是空数组,初始化 elementData 容量为 10;如果不是空数组,则检查 elementData 数组是否已满,如果已满则进行扩容,扩展为原来的 1.5 倍。最后将元素 e 添加到 elementData 数组末尾。

2.get方法

该方法将 返回此列表中指定位置的元素。

public E get(int index) {
	// 数组下标越界检查
	rangeCheck(index);
	return elementData(index);
}

private void rangeCheck(int index) {
	// 如果下标 大于 数组元素数量
	if (index >= size)
	throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

@SuppressWarnings("unchecked")
E elementData(int index) {
	// 返回该元素
	return (E) elementData[index];
}

总结
调用 get(int index) 方法,首先判断下标 index 是否越界,越界则抛异常,没越界则直接返回对应下标的元素。

3.remove方法

该方法 将删除列表中第一次出现的指定元素。

public boolean remove(Object o) {
	// 如果 o 为空
	if (o == null) {
		// 遍历 elementData, 找出 值为 null 的元素并删除
		for (int index = 0; index < size; index++)
			if (elementData[index] == null) {
				fastRemove(index);
				return true;
			}
	// 如果 o 不为空
	} else {
		// 遍历 elementData, 找出 值为 o 的元素并删除
		for (int index = 0; index < size; index++)
			if (o.equals(elementData[index])) {
				fastRemove(index);
				return true;
			}
	}
	return false;
}

// 删除指定下标元素
// 让 index 后的元素整体往前 copy, 然后旧数组的最后一个元素位置赋值为 null
private void fastRemove(int index) {
	modCount++;
	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
}

总结
调用 remove(Object o)方法,需要先判断 o 是否为 null,为空则遍历 elementData,找出值为 null 的元素并删除;如果不为空,遍历 elementData,找出值为 o 的元素并删除。

结束语:每天进步一点点~

你可能感兴趣的:(Java,手撕源码)