ArrayList 基于源码学习

173 阅读5分钟

概括

  • ArrayList是基于数组的列表结构。
  • ArrayList每次插入元素都会检查数组是否需要扩容,一般扩容大小是当前的1.5倍。

ArrayList属性及相关方法

/**
 * 默认初始化数组大小是10。
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空数组
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 默认空数组
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 用于存储元素的数组
 */
transient Object[] elementData;

/**
 * 存取元素的数量
 */
private int size;

/* 下面3个是构造器 */
// 指定初始容量大小
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);
    }
}
// 使用默认空数组
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 传入集合,存储该集合所有元素
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

/**
 * 把数组大小调整的元素数量一致。
 */
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }
}

/**
 * 最大数组大小
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * 避免数组大小溢出变负数
 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

/**
 * 确保读取的索引数组不会越界
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 * 检查是否越界,用于插入元素时的检查
 */
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

核心逻辑追踪

插入元素

add方法

/**
 * 插入元素
 */
public boolean add(E e) {
    // 确保数组可以放下新元素,包含扩容的草走
    ensureCapacityInternal(size + 1);
    // 在最后一个位置插入新的元素
    elementData[size++] = e;
    return true;
}

/**
 * 在指定位置插入元素
 */
public void add(int index, E element) {
    rangeCheckForAdd(index); // 确保index没有溢出
    ensureCapacityInternal(size + 1); // 扩容
    // 把指定索引及后面的元素往后挪一格
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 插入元素
    elementData[index] = element;
    size++;
}

ensureCapacityInternal方法

/**
 * 确保数组容量大于等于minCapacity
 */
private void ensureCapacityInternal(int minCapacity) {
    // 判断数组是否还没初始化
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 重新计算最小容量
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity); // 核心逻辑
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++; // 操作计数+1
    // 如果当前数组容量是否大于最小容量要求
    if (minCapacity - elementData.length > 0) 
        grow(minCapacity); // 扩容
}

grow方法

/**
 * 数组扩容,大小一般扩容为1.5倍
 */
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 扩容为1.5倍
    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);
}

获取元素

/**
 * 获取指定索引元素
 */
public E get(int index) {
    rangeCheck(index); // 确保没有越界
	// 直接通过数组读取
    return elementData(index);
}

删除元素

remove方法

/**
 * 移除指定索引的元素
 */
public E remove(int index) {
    rangeCheck(index); // 检查溢出

    modCount++;
    E oldValue = elementData(index);

    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

    return oldValue;
}
/**
 * 移除指定元素
 */
public boolean remove(Object o) {
    // 都是线性查找,然后移除
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);// 核心删除方法
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index); // 核心删除方法
                return true;
            }
    }
    return false;
}

fastRemove方法

/**
 * 快速删除指定元素
 * 不用检查溢出,不用读取数组。
 */
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; 
}

Iterate机制

内部类Itr

/**
 * ArrayList通过是实现自身的iterator类实现遍历的功能。
 * Itr拥有对应的游标,可以记录已访问的元素索引位置。
 * Itr遍历期间,集合不可被修改,否则将抛异常。
 */
private class Itr implements Iterator<E> {
    int cursor;       // 游标,用于记录访问到的索引
    int lastRet = -1; // 记录最后一个被访问元素的索引
    int expectedModCount = modCount; // 期待mod值,用于判断遍历期间是否有数据被修改

    /**
     * 是否有下一个元素可以访问
     */
    public boolean hasNext() {
        return cursor != size;
    }

    /**
     * 获取下一个元素
     */
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size) // 溢出,游标超过元素数量的大小
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) // 溢出,游标超过数组数量大小
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i]; // 记录最后访问的元素索引
    }

    /**
     * 移除最后一个访问的元素
     */
    public void remove() {
        if (lastRet < 0) 
            throw new IllegalStateException();
        checkForComodification(); // 判断集合是否被修改过

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    /**
     * 判断集合是否被修改过
     */
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

iterate方法

/**
 * 获取遍历器
 */
public Iterator<E> iterator() {
    // 其实就直接生成一个遍历器
    return new Itr();
}

Foreach实现

  • foreach本质上也是调用了iterate方法,再配合hasNext方法和Next方法使用。
  • 由于默认调用了iterate方法,根据面向对象特性,必须有一个接口定义该方法并被ArrayList类实现,该接口就是Iterable接口。
  • iterate方法返回的是Iterator接口,该接口也需要被实现,因此ArrayList实现对应的实现类Itr。

开发者代码

/**
 * 这个是开发者的代码
 */
public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        list.add(i);
    }

    // 这里调用了foreach循环
    for(int i : list){
        System.out.println(i);
    }
}

反编译class文件

/**
 * 这个是编译后再反编译的代码
 */
public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList();

        for(int i = 0; i < 20; ++i) {
            list.add(i);
        }
        
        // 这一部分就是foreach反编译出来的部分
        // 该部分通过iterate方法、hasNext方法、next方法实现foreach循环
        Iterator var4 = list.iterator();
        while(var4.hasNext()) {
            int i = (Integer)var4.next();
            System.out.println(i);
        }

    }