ArrayList源码浅析

ArrayList是Java中实现List接口的一个类,提供常量时间的size、isEmpty、get、set等操作,线性时间的其他操作。ArrayList不是线程安全的,多线程环境下需要手动同步。本文详细分析了ArrayList的构造方法、扩容机制、get、set、add和remove方法。扩容主要通过ensureCapacityInternal在容量不足时以1.5倍进行,get和set方法直接根据数组下标访问,add方法在数组末尾添加元素,remove方法则涉及元素移动。
摘要由CSDN通过智能技术生成

ArrayList

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OoyHVF5C-1615655980593)(C:\Users\10374\AppData\Roaming\Typora\typora-user-images\image-20210314001916143.png)]

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.)

List接口的可调整数组实现。实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,该类还提供了一些方法来操作用于存储列表的内部数组的大小。(这个类大致相当于Vector,不同的是它是不同步的。)

The size, isEmpty, get, set, iterator, and listIterator operations run in constant time. The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking). The constant factor is low compared to that for the LinkedList implementation.

size、isEmpty、get、set、iterator和listIterator操作在常量时间内运行。添加操作运行在平摊常数时间内,即添加n个元素需要O(n)时间。所有其他操作都在线性时间内运行(粗略地说)。与LinkedList实现相比,该常量因子较低。

Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它总是至少与列表大小一样大。当元素被添加到ArrayList时,它的容量会自动增长。除了添加元素具有恒定的摊还时间成本这一事实之外,增长策略的细节没有被指定。

An application can increase the capacity of an ArrayList instance before adding a large number of elements using the ensureCapacity operation. This may reduce the amount of incremental reallocation.

应用程序可以在使用ensureCapacity操作添加大量元素之前增加ArrayList实例的容量。这可能会减少增量重新分配的数量。

Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be “wrapped” using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:
List list = Collections.synchronizedList(new ArrayList(…));
ArrayList是线程不安全的,多线程情况下必须外部同步。

	 /**
     * 默认初试容量
     */
    private static final int DEFAULT_CAPACITY = 10;

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

    /**
     *用于默认大小的空实例的共享空数组实例,以便知道何时膨胀多少添加第一个元素。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储数组列表元素的数组缓冲区,ArrayList的容量就是这个数组缓冲区的长度
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 长度
     *
     * @serial
     */
    private int size;

构造方法

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

我们可以看到对于不为空的arraylist它会帮我们自动初始为大小为10的数组。

扩容机制

    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }
  private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

若为空,就会扩容成大小为10的数组,所以一开始可以直接默认大小为10,就不需要进行扩容。

ensureCapacityInternal在add方法中被调用,找出默认大小和预期最小的容量的最大值,然后尝试着扩容。

  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
	// overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

如果预期最小容量比容量要大,即容量不够时就扩容。

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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);
}

扩容会以原容量大小1.5倍的方式进行扩容,如果容量够大,则以1.5倍的进行扩容。

若不满足,则以最小期望容量进行扩容。

get方法

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

因为是根据数组的,直接获取下标值。

set方法

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

下标超出容量就抛出IndexOutOfBoundsException异常,修改值后返回被修改的值。

add方法

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

和数组中进行插入一个数据一样,该插入位置极其后面的数据将会进行向后平移。

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

该位置后面的值将会进行向前平移,然后删去最后值。

总结

arraylist其实就像个由数组实现的线性表,增删不利,按下标查和改有利。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值