ArrayList 源码解析

ArrayList 是 Java 中实现 List 接口的类,提供了一个可调整大小的数组实现。它的操作如 size、get、set 等在常量时间内完成,而添加元素则在摊销的常量时间内。ArrayList 的容量会随着元素的增加自动增长,初始默认容量为10。在多线程环境下,如果不进行同步控制,可能会引发 ConcurrentModificationException。迭代器和listIterator是快速失败的。
摘要由CSDN通过智能技术生成

List接口的可调整数组实现。实现了所有可选的列表操作,并允许所有元素,包括null。除了实现 List 接口外,该类还提供了操作内部用来存储列表的数组大小的方法。(这个类大致等同于Vector,除了它是非同步的)。
size, isEmpty, get, set, iterator, and listIterator操作在恒定时间内运行。添加操作以摊销的恒定时间运行,也就是说,添加n个元素需要O(n)时间。所有其他的操作都以线性时间运行(大致如此)。与LinkedList的实现相比,恒定系数很低。

这段话的意思是,ArrayList 实现了 List 接口,是一个可以调整的数组,可以存储任意类型的数据,包括 null 值,和操作数组的时间复杂度类似(查找容易,增删难),ArrayList 操作的查找方法,时间复杂度恒定,但是添加元素就是 O(n) 的时间复杂度。

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

在创建 ArryList 对象时如果不分配变量
会调用无参构造,默认的将 elementData 赋值为 0;
这个 elementData 是一个数组缓冲区,ArrayList 的容量是这个数组缓冲区的长度,存储数组列表元素的数组缓冲区。ArrayList的容量是这个数组缓冲区的长度。任何空的 ArrayList 伴随着
elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 成立时,将在添加第一个元素时扩展为DEFAULT_CAPACITY。(这个 DEFAULT_CAPACITY 的大小是 10)

private static final int DEFAULT_CAPACITY = 10;

public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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


下面分析一下,当我们向一个空的 ArrayList 中添加元素的时候会发生些什么吧:
1、首先我们先创建一个 ArrayList arrayList = new ArrayList(); 这时这里的 ArrayList 是一个空的,存储数据的 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
就是为 {} 里面,没有值,值得注意的是,这里的 elementData 是使用 transient 来进行修饰的,我也不知道这个字段是什么意思,所以先插个眼,等以后知道了再回来看。
然后,我们在初始化 ArrayList 的构造函数之前,会先对它的父类进行初始化,因此我们会先进入到 abstractList 的无参构造函数

    /**
     * Sole constructor.  (For invocation by subclass constructors, typically
     * implicit.)
     */
    protected AbstractList() {
    }

我们可以看到它的构造器是被 protected 修饰的,并且从它的构造器中我们可以看到,注释中说的就是唯一的构造函数。(通常用于子类构造函数的调用隐式)

/**
     * Sole constructor.  (For invocation by subclass constructors, typically
     * implicit.)
     */
protected AbstractCollection() {
}

因为 abstractCollection 类没有直接继承的父类,因此我们向上到 Object 超类,调用它的无参构造函数。

/**
     * Constructs a new object.
     */
@HotSpotIntrinsicCandidate
public Object() {}

返回到 abstractCollection 接着执行 abstractCollection 的无参构造,再回到 abstractList 因为 abstractList 有一个成员变量,因此在进行无参构造初始化之前,先对它进行了初始化,

这个列表在结构上被修改的次数。结构修改是那些改变列表大小的修改,或者以这样一种方式打乱列表,使正在进行的迭代可能产生不正确的结果。

该字段由迭代器和listIterator方法返回的迭代器和列表迭代器实现使用。如果该字段的值发生意外变化,迭代器(或列表迭代器)将抛出ConcurrentModificationException,以响应next、remove、previous、set或add操作。这提供了快速失败的行为,而不是在迭代期间面对并发修改时的不确定行为。

子类使用此字段是可选的。如果一个子类希望提供快速失败的迭代器(和列表迭代器),那么它只需要在它的add(int, E)remove(int)方法(以及它覆盖的导致列表结构修改的任何其他方法)中增加这个字段。对add(int, E)remove(int)的单个调用向该字段添加的值不能超过一个,否则迭代器(和列表迭代器)将抛出虚假的concurrentmodificationexception。如果实现不希望提供快速失败迭代器,则可以忽略此字段。
protected transient int modCount = 0;

说实话,这个注释我是有点看不太懂,但是我知道这东西很重要,好像是跟之后再迭代容器中对容器进行增删操作之后会报出的一个异常有关,所以先标记好,迟早会搞懂的。

然后我们初始化了这个 modCount 为 0 之后,开始执行 abstractList 无参构造

/**
     * Constructs an empty list with an initial capacity of ten.
     */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

所以这个构造函数将 elamentData 进行初始化,这个

DEFAULTCAPACITY_EMPTY_ELEMENTDATA


/**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

这就是这个静态常量的定义,看注释我们发现,大致意思就是说用这个值来对我们进行第一次元素添加时的标记。

之后成功回到我们创建的对象的地方。

ArrayList arrayList = new ArrayList();

2、接着调用 add 方法

Appends the specified element to the end of this list.
Params:
e – element to be appended to this list
Returns:
true (as specified by Collection.add)
public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

就是将这个值放置在当前的 elementData 后面,这个我们看注释就能看得很明白

首先因为调用了 add 方法,因此的话就将 modCount++ 变为 1,这个 modCount 是继承自 abstractList 的属性,然后在这个函数内存继续调用 add() 的重载方法,传入三个参数,一个是当前添加的元素的值,一个是当前的 Array List 中存数据的缓冲区,一个是当前容器的容量。

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

然后进入到了这里面来,首先判断 s 也就是我们的 size 是否等于 elementData.length 当然因为 elementData 值为 {} size 值也为 0 因此满足条件,执行 if 条件判断的方法体,调用 grow() 方法来将它返回的值赋值给 elementData 我们来进一步看看这方法是如何执行

private Object[] grow() {
    return grow(size + 1);
}

在这里又继续调用来 grow() 的重载方法,传入 size + 1 就是 1

Increases the capacity to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
Params:
minCapacity – the desired minimum capacity
Throws:
OutOfMemoryErrorif minCapacity is less than zero
private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

增加容量以确保它至少能容纳最小容量参数指定的元素数量。

然后在这里使用 Arrays 的 copyOf 方法将原来的 elementData 重新赋值一份,并指定为新的长度,其实就是扩容,我们继续进入到 newCapacity() 方法中查看,这里传入的 minCapacity 就是 1

Returns a capacity at least as large as the given minimum capacity. Returns the current capacity increased by 50% if that suffices. Will not return a capacity greater than MAX_ARRAY_SIZE unless the given minimum capacity is greater than MAX_ARRAY_SIZE.
Params:
minCapacity – the desired minimum capacity
Throws:
OutOfMemoryErrorif minCapacity is less than zero
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

因为 oldCapacity 就是 0 然后,new Capacity 也是 0,newCapacity - minCapacity <= 0

因为 newCapacity - minCapacity 是等于 -1 小于 0 的,因此就是进入到其方法体 Math.max(DEFAULT_CAPACITY, minCapacity);

DEFAULT_CAPACITY

/**
     * Default initial capacity.
     */
private static final int DEFAULT_CAPACITY = 10;

​ 这时候返回这个 10 到 grow() 函数中,方法体中,将 elementData 的容量扩大

然后回到 add(E e) 方法中,将

elementData[s] = e;

size++ 即可,然后他会返回一个 boolean 类型的值,

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

我们也可以在调用方接收这个变量。


下面这个我有些看不懂,等等看懂了再写吧
注意,这个实现不是同步的。如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了列表,那么必须在外部进行同步。(结构性修改是指任何增加或删除一个或多个元素的操作,或者明确地调整支持数组的大小;仅仅设置一个元素的值并不是结构性修改。) 这通常是通过在一些自然封装了列表的对象上进行同步来实现的。如果没有这样的对象,应该使用 Collections.synchronizedList 方法对列表进行 “包装”。这最好在创建时完成,以防止意外地对列表进行非同步访问。
List list = Collections.synchronizedList(new ArrayList(…))。
这个类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时候,列表的结构被修改,除了通过迭代器自己的remove或add方法,迭代器将抛出一个ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在未来某个不确定的时间冒着任意的、不确定的行为。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值