Java容器学习--ArrayList源码分析

ArrayList 内部采用数组实现,是一种顺序存储方式,并且支持随机访问。本文分析基于 JDK 1.8.0_151 版本。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}

ArrayList 继承于AbstractList并且实现了ListRandomAccessCloneableSerializable接口。


属性

// 序列化ID
private static final long serialVersionUID = 8683452581122892189L;

// 默认初始化容量(只有执行 add 操作时才执行初始化)
private static final int DEFAULT_CAPACITY = 10;

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

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

// 以上两个空数组只是为了区别于调用不同的构造方法

// 存放数据的内部数组(不参与序列化)
transient Object[] elementData;

// ArrayList 中有效数据的个数(并不一定就是elementData.length)
private int size;

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

方法

构造方法

// 构造具有指定初始容量的空列表
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;
}

// 构造一个包含指定 collection 的元素的列表,这些元素是按照该collection 的迭代器返回它们的顺序排列的
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 该 bug 分析见 http://blog.csdn.net/x_iya/article/details/78313756
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // c.toArray 不一定返回 Object[],也有可能是 String[](List<String> list = new ArrayList<>(Arrays.asList("111", "222"));)
        if (elementData.getClass() != Object[].class)
        //  转换为Object[]
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

在执行add相关操作时,需要调用ensureCapacityInternal方法以确保elementData 有足够的空间保存元素。

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;
 }

改&查

ArrayList的修改和获取元素的方法相当简单,就是对elementData数组进行相应的操作罢了。

public E set(int index, E element) {
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

其他

扩容

扩容操作基本上是以数组作为其存储结构的类的核心了(String、ArrayList、StringBuilder,StringBuffer)。其他的所有操作无非就是围绕这个可变长的数组。

调用关系:

ensureCapacity() //可以由用户显式调用
ensureCapacityInternal() // 由 ArrayList 内部隐式调用
        --> ensureExplicitCapacity()
                    --> grow()

grow 方法:

    /**
     * 增加容量以确保它至少可以容纳最小容量参数 minCapacity 指定的元素数量
     * minCapacity: 数组需要的最小容量值
     * newCapacity: 就是扩容后实际的容量值
     * 每次自动扩容都是扩大原有容量 1.5 倍
     * 当自动扩容后的容量值仍然小于所需要的容量时,就直接扩容到所需容量值
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 可以看到首先扩展为原来的 1.5 倍
        if (newCapacity - minCapacity < 0) // 防止溢出
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity); // 设置大小为 Integer.MAX_VALUE
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

ensureExplicitCapacity 方法:

    /**
     * 保证扩容值大于原数组长度才执行 grow() 方法
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

ensureCapacityInternal 方法:

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

ensureCapacityInternal()方法首先判断该数组此时是不是默认初始数组,如果是的话就判断一下minCapacity是否大于10,如果不是就扩容到初始默认数组长度10,如果是就直接扩容到minCapacity值的长度。

用户所能使用的只有一个公有方法,也就是说可以由用户显式执行扩容操作:

    /**
     * 如有必要,增加此 ArrayList 实例的容量,以确保它至少能够容纳最小容量参数 minCapacity 所指定的元素个数
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

ensureCapacityInternal 隐式扩容方法在容器添加元素的时候被调用,也就是“懒扩容–>只有在真正添加元素的时候进行扩容操作”。


使用 ArrayList 需要注意的地方:

  • 1.ArrayList 是基于数组的方式实现的,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
  • 2.ArrayList在插入元素时,可能会进行数组的扩容,但是在删除元素时却不会减小数组的容量,如果希望减小数组的容量,可使用 trimToSize 方法,在查找元素要遍历数组时,对非null元素使用equals方法,对null元素使用==。
  • 3.扩充容量的方法 ensureCapacityInternal。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当 容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍,如果设置后的新容量还不够,则直接把新容量设置为传入的参数(也就是所需的容 量),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList
  • 4.ArrayList不是线程安全的。

参考:
https://zhuanlan.zhihu.com/p/27873515

https://zhuanlan.zhihu.com/p/28545284

https://mjd507.github.io/2017/04/09/Data-Structure-ArrayList-SourceCode/

http://www.cnblogs.com/skywang12345/p/3308556.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值