Java数据结构详解(三)-ArrayList

ArrayList源码详解

一,ArrayList字段

    /**
     * 默认初始化的容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 实例共享的空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
        默认大小的实例共享的空数组,与上面的EMPTY_ELEMENTDATA区别是在第一个元素被添加时可以知道数组扩大了多少
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
    存储数组列表元素的数组缓冲区。数组列表的容量是这个数组缓冲区的长度。当添加第一个元素时,带有元素Data == DEFAULTCAPACITY empty ELEMENTDATA的空数组将会扩展到默认容量。
     */
    transient Object[] elementData; 

    /**
     * 数组列表的逻辑大小(它包含的元素的数目)
     */
    private int size;

二,ArrayList的构造方法

第一个构造方法:
这里写图片描述

构造一个初始容量为 10 的空列表
至于为什么为10 这个在后面add方法里再讲。

第二个构造方法:
这里写图片描述
第二个构造方法支持传入一个int类型的参数,用作初始化数组大小

第三个构造方法:

这里写图片描述
第三个构造方法有点复杂,传入参数为Collection c。

第一步调用c.toArray()方法。toArray方法存在collection接口中,其返回

值是一个Object数组。


public interface Collection<E> extends Iterable<E> {

//将包含该集合中的所有元素的数组e转换
 Object[] toArray();

所以其子类肯定有自己的具体实现。我们只需要调用他就好。

调用完toArray方法之后会初始化ArrayList。初始化的值为toArray的返

回值。

接下来会判断传入的参数c转换成数组之后是不是为空数组。如果是空数

组,则初始化ArrayList,初始值为空数组。

if ((size = elementData.length) != 0) {
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
}

如果不为空,则判断elementData是不是Object类型的数组,因为有时候toArray有可能会错误的返回一个不是Object类型的数组,所以这个时候要判断一下。如果是,则构造方法结束。如果不是 ,则调用Array.copyof方法将旧数组转换为Object类型。


ArrayList的增删改查

public boolean add(E e) {}
这里写图片描述

在代码elementData[size++] = e; 之前调用了ensureCapacityInternal(size + 1); 这个方法,该方法确保了数组空间的容量是不是够用,以及数组空间的扩容。


ensureCapacityInternal(minCapacity);方法
这里写图片描述

这个方法其实就是判断是不是用了空构造器new的对象,如果是,那么在这个方法 里初始化了一个大小为10 的数组。

接着调用方法:

ensureExplicitCapacity(minCapacity);

该方法确保了当数组元素数量超过了数组容量时 数组扩容的问题。


ensureExplicitCapacity(minCapacity);方法
这里写图片描述

如果数组元素数量没有超过数组的长度,则不进行扩容。
方法ensureExplicitCapacity()结束。紧跟着
ensureCapacityInternal()方法结束。最后进入add方法执行

elementData[size++] = e;

如果数组元素数量超过数组的长度,则进行扩容,调用grow();方法。


grow(minCapacity);方法
这里写图片描述


Arrays.copyOf();方法
这里写图片描述
可以看出 扩容需要复制 之前的所有的数组元素。所以说扩容性能很差。

到这里add()方法就结束了。


public void add(int index, E element) { }

这里写图片描述
可以看出 add(int index, E element) 方法在index位置插入数据后,index后面的所有元素都要向右移动一位。这会导致性能下降。index的值越小。数组元素越多。则性能越低。如果index正好等于size,则和add()方法花费 的时间一样。
rangeCheckForAdd()
这里写图片描述

add(int index, E element) 结束


public E remove(int index) {}
这里写图片描述

public boolean remove(Object o) {}
这里写图片描述
根据传入参数删除数据。
如果传入参数 为null,则for循环遍历数组,找到第一个为null的数组,获取下标值。然后通过fastRemove(index) 删除数据。
如果传入参数不为null 则for循环遍历数组,通过equals方法判断数组的数据是不是和传入数据相等。然后获取下标值 通过fastRemove(index)删除数据。

private void fastRemove(int index) {}
这里写图片描述

删除方法结束


public E set(int index, E element) {}
这里写图片描述

1,检查下标是否超出范围
2,拿到下标为index的旧数据
3,把下标为index设置成新数据element
4,返回旧数据


public E get(int index) {}
这里写图片描述
1,检查下标 范围
2,返回数据


ArrayList的时间复杂度

可以看出ArrayList的 add ,remove 涉及到底层数组的复制,所以效率低。
而 Set/add 方法效率较高。

ArrayList存/取元素效率非常的高(get/set),时间复杂度是O(1),而查找,插入和删除元素效率不太高,时间复杂度为O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值