ArrayList底层源码解析

​ ArrayList是jdk自带的,有序的集合。首先,我们来看源码中类上的注释信息:

源码类头注释信息

		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.)
		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.
		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.
		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.
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(...));
		The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
		Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
		This class is a member of the Java Collections Framework.
Since:
	1.2
	See Also:
		Collection, List, LinkedList, Vector
Author:
Josh Bloch, Neal Gafter

注释翻译:

		List接口的可调整数组实现。实现所有可选列表操作,并允许所有元素,包括null。除了实现List接口之外,这个类还提供了一些方法来操作内部用于存储列表的数组的大小。(这个类大致相当于Vector,只是它是不同步的。)
		size、isEmpty、get、set、iterator和listIterator操作在常量时间内运行。添加操作在平摊常数时间内运行,即添加n个元素需要O(n)时间。所有其他操作都在线性时间内运行(粗略地说)。与LinkedList实现相比,常数因子较低。
		每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它总是至少和列表大小一样大。当元素被添加到ArrayList中时,它的容量会自动增长。除了添加一个元素具有恒定的平摊时间成本这一事实之外,没有指定增长策略的细节。
		应用程序可以在添加大量元素之前使用ensureCapacity操作来增加ArrayList实例的容量。这可能会减少增量再分配的数量。
注意,这个实现是不同步的。如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了该列表,则必须在外部进行同步。(结构修改是任何添加或删除一个或多个元素的操作,或者显式地调整后台数组的大小;仅仅设置元素的值并不是对结构的修改。)这通常是通过对一些自然封装列表的对象进行同步来完成的。如果不存在这样的对象,则应该使用集合“包装”列表。synchronizedList方法。这最好在创建时完成,以防止对列表的意外非同步访问:
		List list = Collections.synchronizedList(new ArrayList(...));
		这个类的iterator和listIterator方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时间,以除通过迭代器自己的remove或add方法之外的任何方式修改列表,迭代器将抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意的、不确定的行为的风险。
		请注意,不能保证迭代器的快速故障行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬保证。快速失败迭代器在尽最大努力的基础上抛出ConcurrentModificationException。因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误。
		该类是Java集合框架的成员。
自:
1.2
参见:
Collection, List, LinkedList, Vector
作者:
乔什·布洛赫,尼尔·盖特

​ 从上面的信息里面,我们可以看出ArrayList的一些特性:

​ 可以插入null。

​ 相当于Vector,但非线程安全。

​ 可以通过Collections的synchronizedList方法进行包装,实现线程安全。

UML图

常用方法解析

public boolean add(E e)

/**
 * 往集合中添加元素,
 */
public boolean add(E e) {
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  elementData[size++] = e;
  return true;
}

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

/**
 * 计算容量大小
 *   如果内部数组为空数组,则指定容量为
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    return Math.max(DEFAULT_CAPACITY, minCapacity);
  }
  return minCapacity;
}

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); //以原来的容量的1.5倍进行扩容
  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);
}

private static int hugeCapacity(int minCapacity) {
  if (minCapacity < 0) // overflow,超出了int的MAX_VALUE,所以变成了小数
    throw new OutOfMemoryError();
  return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
  MAX_ARRAY_SIZE;
}

解析说明:

在新增元素的时候,首先会去判断当前数组容量的大小,如果容量不够,就会进行扩容,默认情况下是以原来容量的1.5倍进行扩充的。扩容就是新生成一个数组,然后先把原来的元素都拷贝到新的数组里面,然后再把新增的元素放在size位上,然后size加一

public boolean remove(Object o)

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

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; // clear to let GC do its work
}

解析说明:

移除操作,首先是找到对应的元素对应的索引,找不到则直接返回false。如果找到了,则判断是否需要移动元素,只要不是移除的最末尾的元素,都会涉及到目标索引之后位置元素的移动。最后再把最末尾的元素清空,以便gc处理

和Vector区别

UML图

相同点:

​ 类的结构完全一样,内部结构也差不多,都是有序可重复的集合

不同点

​ Vector内部有一个扩容参数,是一个int值,默认情况为0,在构造是可以设置。在扩容的时候,如果设置了这个扩容参数,扩容是就会在原来的容量基础上加上这个数,不够会对原来的容量进行翻倍处理。而ArrayList默认是按1.5进行扩容的。

​ 当然,最大的区别在于Vector是线程安全的,它内部的很多方法上添加了synchronized修饰符,从而实现了线程同步。

posted @ 2023-09-26 10:24  山有木兮树有枝  阅读(11)  评论(0)    收藏  举报