Java 集合 - ArrayList

209 阅读3分钟

Java 集合- ArrayList 源码浅析

基本使用

新建一个Java类,以最基本的用法来走一遍 ArrayList 是如何运行的

package demo;

import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("demo");
        String s = list.get(0);
        list.remove(0);
    }
}

总共分为4步

  1. 声明一个 ArrayList
  2. 添加一个元素
  3. 获取一个元素
  4. 删除一个元素
开始逐步分析源码
  • 声明一个ArrayList

    进入ArrayList的构造函数

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
        // ...
    	transient Object[] elementData;
        
        // ...
    	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
        
        // ...
    	public ArrayList() {
    		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    	}
        
        // ...
    }
    

    其中,elementData 为实际上 ArrayList 存储数据的 Object 数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为一个空数组,所以初始化的结果就是将一个空数组赋值给了实际存储数组的数组,其他的什么也没有做

  • 添加一个元素

    跟踪代码进入 add 函数,发现 add 函数如下所示

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    

    其中 modeCount 的作用是记录对这个 list 的修改次数,包括 add/delete 等操作。通过注释可以发现,这个值的作用是用在迭代器中,如果在迭代的过程中修改了数组,则抛出 ConcurrentModificationException 这个异常,例子如下

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("demo1");
        list.add("demo2");
        list.add("demo3");
    
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next);
            if (next.equals("demo2")) {
                list.remove(next);
             }
        }
    }
    

    进入最主要的 add 函数,发现接受 3 个参数,当前需要添加的值,目前已经存的数据,当前的长度

    private void add(E e, Object[] elementData, int s) {
       	if (s == elementData.length)
        	elementData = grow();
       	elementData[s] = e;
       	size = s + 1;
    }
    

    其中首先判断当前的 elementData.lengthlist.size 的大小,如果两个值相等,则意味着 elementData 已经被填满,需要对 elementData 数组进行扩容,否则直接可以在 elementData 中的第 size 位添加元素,并把 size 加1即可

    首次添加元素,通过上面的构造函数可知,此时的 elementData 为空数组,且当前 size 为0,满足条件,需要对 elementData 数组进行扩容,进入 grow()

    private Object[] grow() {
        return grow(size + 1);
    }
    private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 非第一次扩容
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } 
        // 第一次扩容
        else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }
    

    参数 size + 1表示此时需要扩容的最小容量 minCapacity

    如果是第一次扩容,进入 else 分支,可以看到是初始化 DEFAULT_CAPACITYminCapacity 的最大值,其中 DEFAULT_CAPACITY 的值是10 ,于是 ArrayList 的初始化容量为 10 就是从这里得出

    如果不是第一次扩容,则进入 if 分支,可以看到新的容量是通过 ArraysSupport.newLength() 得出,进入对应的函数

    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
    	// assert oldLength >= 0
    	// assert minGrowth > 0
    
    	int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
    	if (newLength - MAX_ARRAY_LENGTH <= 0) {
    		return newLength;
    	}
    	return hugeLength(oldLength, minGrowth);
    }
    

    可以看到该函数接受三个参数 oldLength 当前长度,minGrowth 最小扩容长度,prefGrowth最佳长度,此时最佳长度为 oldCapacity >> 1 ,即当前长度的一半。

    可以看到通过这个函数,返回的新长度为 prefGrowth minGrowth 的最大值再加上当前长度,即当前长度的1.5倍,即 ArrayList 默认的 1.5 倍扩容来源于此。

    hugeLength() 中,对于特别大的扩容数量做了单独处理,即 Integer.MAX_VALUE - 8

    由此 ArrayList 的 add 流程就基本结束。

  • 获取一个元素

    public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }
    

    首先检查传入的 index 是否超过 size

    然后进入 elementData(index) 函数

    E elementData(int index) {
        return (E) elementData[index];
    }
    

    直接通过获取数组的下标返回所需的值

  • 删除一个元素

    public E remove(int index) {
    	Objects.checkIndex(index, size);
    	final Object[] es = elementData;
    
    	@SuppressWarnings("unchecked") E oldValue = (E) es[index];
    	fastRemove(es, index);
    
    	return oldValue;
    }
    

    首先同样的,需要校验传入 index 是否超过 size

    然后通过下标将需要删除的元素取出

    然后进入 fastRemove(es, index) 将该元素删除,进入对应函数

    private void fastRemove(Object[] es, int i) {
    	modCount++;
    	final int newSize;
    	if ((newSize = size - 1) > i)
    		System.arraycopy(es, i + 1, es, i, newSize - i);
    	es[size = newSize] = null;
    }
    

    与 add 同样的,首先需要对 modCount 进行 +1

    其中 (newSize = size - 1) > i 的作用是判断当前的删除元素是否为最后一个,如果是的话,则需要将该元素的后面所有元素前移一位

    最后将最后一位置 null