ArrayList 源码全解析:动态扩容、数组互转与底层原理

上一篇讲了数组和链表的基础概念,这篇直接进入正题------ArrayList。

面试里关于 ArrayList 的问题,问法五花八门,但基本都绕不开这几类:

  • ArrayList 底层是怎么实现的?
  • 它的扩容机制是什么样的?
  • new ArrayList(10) 扩容了几次?
  • 数组和 List 怎么互相转换,转换之后互相修改有没有影响?

这些问题的答案全在源码里。JDK 1.8 的 ArrayList 源码不算复杂,跟着走一遍就能搞明白。

ArrayList 的底层数据结构

一句话:ArrayList 底层是用动态数组实现的。

java 复制代码
transient Object[] elementData;
private int size;

elementData 就是那个存数据的数组,size 是实际装了多少元素。注意 size 和 elementData.length 是两回事------前者是"装了多少",后者是"能装多少"(容量)。

成员变量

先看 ArrayList 里几个关键的成员变量:

java 复制代码
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;

// 空数组实例,用于指定容量为 0 时
private static final Object[] EMPTY_ELEMENTDATA = {};

// 空数组实例,用于无参构造,跟 EMPTY_ELEMENTDATA 区分开
// 目的是:第一次添加元素时,知道该扩容到 DEFAULT_CAPACITY(10)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 真正存数据的数组
transient Object[] elementData;

// 实际元素个数
private int size;

DEFAULTCAPACITY_EMPTY_ELEMENTDATA 和 EMPTY_ELEMENTDATA 虽然都是空数组,但用途不同。源码用前者来标记"这个 ArrayList 是通过无参构造创建的",这样第一次 add 时才能把容量初始化为默认的 10。
#mermaid-svg-A9m90fKmq3wIitfb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A9m90fKmq3wIitfb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A9m90fKmq3wIitfb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A9m90fKmq3wIitfb .error-icon{fill:#552222;}#mermaid-svg-A9m90fKmq3wIitfb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A9m90fKmq3wIitfb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A9m90fKmq3wIitfb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A9m90fKmq3wIitfb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A9m90fKmq3wIitfb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A9m90fKmq3wIitfb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A9m90fKmq3wIitfb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A9m90fKmq3wIitfb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A9m90fKmq3wIitfb .marker.cross{stroke:#333333;}#mermaid-svg-A9m90fKmq3wIitfb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A9m90fKmq3wIitfb p{margin:0;}#mermaid-svg-A9m90fKmq3wIitfb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A9m90fKmq3wIitfb .cluster-label text{fill:#333;}#mermaid-svg-A9m90fKmq3wIitfb .cluster-label span{color:#333;}#mermaid-svg-A9m90fKmq3wIitfb .cluster-label span p{background-color:transparent;}#mermaid-svg-A9m90fKmq3wIitfb .label text,#mermaid-svg-A9m90fKmq3wIitfb span{fill:#333;color:#333;}#mermaid-svg-A9m90fKmq3wIitfb .node rect,#mermaid-svg-A9m90fKmq3wIitfb .node circle,#mermaid-svg-A9m90fKmq3wIitfb .node ellipse,#mermaid-svg-A9m90fKmq3wIitfb .node polygon,#mermaid-svg-A9m90fKmq3wIitfb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A9m90fKmq3wIitfb .rough-node .label text,#mermaid-svg-A9m90fKmq3wIitfb .node .label text,#mermaid-svg-A9m90fKmq3wIitfb .image-shape .label,#mermaid-svg-A9m90fKmq3wIitfb .icon-shape .label{text-anchor:middle;}#mermaid-svg-A9m90fKmq3wIitfb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A9m90fKmq3wIitfb .rough-node .label,#mermaid-svg-A9m90fKmq3wIitfb .node .label,#mermaid-svg-A9m90fKmq3wIitfb .image-shape .label,#mermaid-svg-A9m90fKmq3wIitfb .icon-shape .label{text-align:center;}#mermaid-svg-A9m90fKmq3wIitfb .node.clickable{cursor:pointer;}#mermaid-svg-A9m90fKmq3wIitfb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A9m90fKmq3wIitfb .arrowheadPath{fill:#333333;}#mermaid-svg-A9m90fKmq3wIitfb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A9m90fKmq3wIitfb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A9m90fKmq3wIitfb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A9m90fKmq3wIitfb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A9m90fKmq3wIitfb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A9m90fKmq3wIitfb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A9m90fKmq3wIitfb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A9m90fKmq3wIitfb .cluster text{fill:#333;}#mermaid-svg-A9m90fKmq3wIitfb .cluster span{color:#333;}#mermaid-svg-A9m90fKmq3wIitfb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A9m90fKmq3wIitfb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A9m90fKmq3wIitfb rect.text{fill:none;stroke-width:0;}#mermaid-svg-A9m90fKmq3wIitfb .icon-shape,#mermaid-svg-A9m90fKmq3wIitfb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A9m90fKmq3wIitfb .icon-shape p,#mermaid-svg-A9m90fKmq3wIitfb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A9m90fKmq3wIitfb .icon-shape .label rect,#mermaid-svg-A9m90fKmq3wIitfb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A9m90fKmq3wIitfb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A9m90fKmq3wIitfb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A9m90fKmq3wIitfb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} new ArrayList()
elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
第一次 add()
识别到是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
扩容到 DEFAULT_CAPACITY = 10
new ArrayList(0)
elementData = EMPTY_ELEMENTDATA
第一次 add()
扩容到 1(按需扩)
new ArrayList(20)
elementData = new Object20
直接使用,无需扩容

构造方法

ArrayList 有三个构造方法:

java 复制代码
// 无参构造:空集合,第一次 add 时才初始化容量为 10
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 指定初始容量
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(Collection<? extends E> c) {
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        elementData = EMPTY_ELEMENTDATA;
    }
}

无参构造是最常用的,注意它并没有直接创建长度为 10 的数组,而是用了"懒加载"------等你真的往里面放东西时才分配空间。

add 方法:添加元素的全过程

java 复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保容量够用
    elementData[size++] = e;           // 放入元素,size++
    return true;
}

整个 add 的核心在 ensureCapacityInternal 这一行。它内部调了三个方法,逐层拆开来看:

java 复制代码
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 计算容量:如果是默认空数组,至少给 DEFAULT_CAPACITY
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++;  // 结构化修改计数,用于 fail-fast

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

源码里那个 modCount++ 不是可有可无的。modCount 记录的是 ArrayList 结构化修改 的次数------添加、删除、扩容等改变数组结构的操作。它的核心用途是 fail-fast 机制

java 复制代码
// 用 for-each 或迭代器遍历时,如果另一个线程修改了 ArrayList
// 迭代器的 next() 方法会检查 modCount 是否和预期值一致
// 不一致就直接抛 ConcurrentModificationException,立刻失败

这样做的好处是"宁可快速报错,也不带着脏数据继续执行"。比如你在遍历一个 List 的时候,另一个线程删除了一个元素,如果没有 fail-fast,你可能读到不一致的状态,排查起来比直接抛异常麻烦得多。

注意,fail-fast 并不能保证一定捕获并发修改------它只是"尽力而为"的检测。所以不要在遍历时直接调用 list.remove(),要用迭代器的 remove() 方法,或者用 CopyOnWriteArrayList 这类并发安全的集合。

把整个添加流程用 mermaid 画出来:
#mermaid-svg-PQ7kRxz9SD1ABtNR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PQ7kRxz9SD1ABtNR .error-icon{fill:#552222;}#mermaid-svg-PQ7kRxz9SD1ABtNR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PQ7kRxz9SD1ABtNR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .marker.cross{stroke:#333333;}#mermaid-svg-PQ7kRxz9SD1ABtNR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PQ7kRxz9SD1ABtNR p{margin:0;}#mermaid-svg-PQ7kRxz9SD1ABtNR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster-label text{fill:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster-label span{color:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster-label span p{background-color:transparent;}#mermaid-svg-PQ7kRxz9SD1ABtNR .label text,#mermaid-svg-PQ7kRxz9SD1ABtNR span{fill:#333;color:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .node rect,#mermaid-svg-PQ7kRxz9SD1ABtNR .node circle,#mermaid-svg-PQ7kRxz9SD1ABtNR .node ellipse,#mermaid-svg-PQ7kRxz9SD1ABtNR .node polygon,#mermaid-svg-PQ7kRxz9SD1ABtNR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .rough-node .label text,#mermaid-svg-PQ7kRxz9SD1ABtNR .node .label text,#mermaid-svg-PQ7kRxz9SD1ABtNR .image-shape .label,#mermaid-svg-PQ7kRxz9SD1ABtNR .icon-shape .label{text-anchor:middle;}#mermaid-svg-PQ7kRxz9SD1ABtNR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .rough-node .label,#mermaid-svg-PQ7kRxz9SD1ABtNR .node .label,#mermaid-svg-PQ7kRxz9SD1ABtNR .image-shape .label,#mermaid-svg-PQ7kRxz9SD1ABtNR .icon-shape .label{text-align:center;}#mermaid-svg-PQ7kRxz9SD1ABtNR .node.clickable{cursor:pointer;}#mermaid-svg-PQ7kRxz9SD1ABtNR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .arrowheadPath{fill:#333333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PQ7kRxz9SD1ABtNR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PQ7kRxz9SD1ABtNR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PQ7kRxz9SD1ABtNR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster text{fill:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR .cluster span{color:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PQ7kRxz9SD1ABtNR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PQ7kRxz9SD1ABtNR rect.text{fill:none;stroke-width:0;}#mermaid-svg-PQ7kRxz9SD1ABtNR .icon-shape,#mermaid-svg-PQ7kRxz9SD1ABtNR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PQ7kRxz9SD1ABtNR .icon-shape p,#mermaid-svg-PQ7kRxz9SD1ABtNR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PQ7kRxz9SD1ABtNR .icon-shape .label rect,#mermaid-svg-PQ7kRxz9SD1ABtNR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PQ7kRxz9SD1ABtNR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PQ7kRxz9SD1ABtNR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PQ7kRxz9SD1ABtNR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是(无参构造)



add(E e)
ensureCapacityInternal(size + 1)
calculateCapacity
elementData 是

DEFAULTCAPACITY_EMPTY_ELEMENTDATA?
返回 max(10, minCapacity)
返回 minCapacity
ensureExplicitCapacity
需要的容量 > 当前数组长度?
grow() 扩容
不扩容
elementDatasize++ = e

扩容机制:grow 方法

java 复制代码
private void grow(int minCapacity) {
    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);
    elementData = Arrays.copyOf(elementData, newCapacity);  // 数组拷贝
}

核心公式:newCapacity = oldCapacity + (oldCapacity >> 1),也就是 原来的 1.5 倍

老容量 新容量
10 15
15 22
22 33

每次扩容都会调用 Arrays.copyOf,这会创建一个新数组,然后把老数组里的元素挨个拷过去。这个过程是 O(n) 的。
#mermaid-svg-ymqytZyyDZ1AJdZH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ymqytZyyDZ1AJdZH .error-icon{fill:#552222;}#mermaid-svg-ymqytZyyDZ1AJdZH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ymqytZyyDZ1AJdZH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ymqytZyyDZ1AJdZH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ymqytZyyDZ1AJdZH .marker.cross{stroke:#333333;}#mermaid-svg-ymqytZyyDZ1AJdZH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ymqytZyyDZ1AJdZH p{margin:0;}#mermaid-svg-ymqytZyyDZ1AJdZH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster-label text{fill:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster-label span{color:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster-label span p{background-color:transparent;}#mermaid-svg-ymqytZyyDZ1AJdZH .label text,#mermaid-svg-ymqytZyyDZ1AJdZH span{fill:#333;color:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH .node rect,#mermaid-svg-ymqytZyyDZ1AJdZH .node circle,#mermaid-svg-ymqytZyyDZ1AJdZH .node ellipse,#mermaid-svg-ymqytZyyDZ1AJdZH .node polygon,#mermaid-svg-ymqytZyyDZ1AJdZH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ymqytZyyDZ1AJdZH .rough-node .label text,#mermaid-svg-ymqytZyyDZ1AJdZH .node .label text,#mermaid-svg-ymqytZyyDZ1AJdZH .image-shape .label,#mermaid-svg-ymqytZyyDZ1AJdZH .icon-shape .label{text-anchor:middle;}#mermaid-svg-ymqytZyyDZ1AJdZH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ymqytZyyDZ1AJdZH .rough-node .label,#mermaid-svg-ymqytZyyDZ1AJdZH .node .label,#mermaid-svg-ymqytZyyDZ1AJdZH .image-shape .label,#mermaid-svg-ymqytZyyDZ1AJdZH .icon-shape .label{text-align:center;}#mermaid-svg-ymqytZyyDZ1AJdZH .node.clickable{cursor:pointer;}#mermaid-svg-ymqytZyyDZ1AJdZH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ymqytZyyDZ1AJdZH .arrowheadPath{fill:#333333;}#mermaid-svg-ymqytZyyDZ1AJdZH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ymqytZyyDZ1AJdZH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ymqytZyyDZ1AJdZH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ymqytZyyDZ1AJdZH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ymqytZyyDZ1AJdZH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ymqytZyyDZ1AJdZH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster text{fill:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH .cluster span{color:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ymqytZyyDZ1AJdZH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ymqytZyyDZ1AJdZH rect.text{fill:none;stroke-width:0;}#mermaid-svg-ymqytZyyDZ1AJdZH .icon-shape,#mermaid-svg-ymqytZyyDZ1AJdZH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ymqytZyyDZ1AJdZH .icon-shape p,#mermaid-svg-ymqytZyyDZ1AJdZH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ymqytZyyDZ1AJdZH .icon-shape .label rect,#mermaid-svg-ymqytZyyDZ1AJdZH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ymqytZyyDZ1AJdZH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ymqytZyyDZ1AJdZH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ymqytZyyDZ1AJdZH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 老数组

0, 1, 2, 3, ..., 14

容量 15
Arrays.copyOf
新数组

0, 1, 2, 3, ..., 14, null, null, ..., null

容量 22

扩容过程的三个阶段

new ArrayList() 无参构造,然后依次添加元素,容量变化分三个阶段。PPT 里专门用了 3 页来演示这个过程:

阶段一:第 1 次 add
#mermaid-svg-iLbn4ZxifUEiYgTd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iLbn4ZxifUEiYgTd .error-icon{fill:#552222;}#mermaid-svg-iLbn4ZxifUEiYgTd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iLbn4ZxifUEiYgTd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iLbn4ZxifUEiYgTd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iLbn4ZxifUEiYgTd .marker.cross{stroke:#333333;}#mermaid-svg-iLbn4ZxifUEiYgTd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iLbn4ZxifUEiYgTd p{margin:0;}#mermaid-svg-iLbn4ZxifUEiYgTd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster-label text{fill:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster-label span{color:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster-label span p{background-color:transparent;}#mermaid-svg-iLbn4ZxifUEiYgTd .label text,#mermaid-svg-iLbn4ZxifUEiYgTd span{fill:#333;color:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd .node rect,#mermaid-svg-iLbn4ZxifUEiYgTd .node circle,#mermaid-svg-iLbn4ZxifUEiYgTd .node ellipse,#mermaid-svg-iLbn4ZxifUEiYgTd .node polygon,#mermaid-svg-iLbn4ZxifUEiYgTd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iLbn4ZxifUEiYgTd .rough-node .label text,#mermaid-svg-iLbn4ZxifUEiYgTd .node .label text,#mermaid-svg-iLbn4ZxifUEiYgTd .image-shape .label,#mermaid-svg-iLbn4ZxifUEiYgTd .icon-shape .label{text-anchor:middle;}#mermaid-svg-iLbn4ZxifUEiYgTd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iLbn4ZxifUEiYgTd .rough-node .label,#mermaid-svg-iLbn4ZxifUEiYgTd .node .label,#mermaid-svg-iLbn4ZxifUEiYgTd .image-shape .label,#mermaid-svg-iLbn4ZxifUEiYgTd .icon-shape .label{text-align:center;}#mermaid-svg-iLbn4ZxifUEiYgTd .node.clickable{cursor:pointer;}#mermaid-svg-iLbn4ZxifUEiYgTd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iLbn4ZxifUEiYgTd .arrowheadPath{fill:#333333;}#mermaid-svg-iLbn4ZxifUEiYgTd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iLbn4ZxifUEiYgTd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iLbn4ZxifUEiYgTd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iLbn4ZxifUEiYgTd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iLbn4ZxifUEiYgTd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iLbn4ZxifUEiYgTd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster text{fill:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd .cluster span{color:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iLbn4ZxifUEiYgTd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iLbn4ZxifUEiYgTd rect.text{fill:none;stroke-width:0;}#mermaid-svg-iLbn4ZxifUEiYgTd .icon-shape,#mermaid-svg-iLbn4ZxifUEiYgTd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iLbn4ZxifUEiYgTd .icon-shape p,#mermaid-svg-iLbn4ZxifUEiYgTd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iLbn4ZxifUEiYgTd .icon-shape .label rect,#mermaid-svg-iLbn4ZxifUEiYgTd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iLbn4ZxifUEiYgTd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iLbn4ZxifUEiYgTd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iLbn4ZxifUEiYgTd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
是,需扩容
第1次 add
ensureCapacityInternal(1)
elementData 是

默认空数组?
calculateCapacity

返回 max(10,1)=10
ensureExplicitCapacity(10)
10 > 数组长度 0?
grow()

oldCap=0 → newCap=10
Arrays.copyOf

创建新数组
容量 10, size 1

关键点:无参构造的 ArrayList,第一次 add 时因为 elementData 还是 DEFAULTCAPACITY_EMPTY_ELEMENTDATAcalculateCapacity 直接返回 DEFAULT_CAPACITY=10,触发扩容到 10。

阶段二:第 2~10 次 add
#mermaid-svg-mDlNANH1fYrVfjC7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mDlNANH1fYrVfjC7 .error-icon{fill:#552222;}#mermaid-svg-mDlNANH1fYrVfjC7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mDlNANH1fYrVfjC7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mDlNANH1fYrVfjC7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mDlNANH1fYrVfjC7 .marker.cross{stroke:#333333;}#mermaid-svg-mDlNANH1fYrVfjC7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mDlNANH1fYrVfjC7 p{margin:0;}#mermaid-svg-mDlNANH1fYrVfjC7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster-label text{fill:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster-label span{color:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster-label span p{background-color:transparent;}#mermaid-svg-mDlNANH1fYrVfjC7 .label text,#mermaid-svg-mDlNANH1fYrVfjC7 span{fill:#333;color:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 .node rect,#mermaid-svg-mDlNANH1fYrVfjC7 .node circle,#mermaid-svg-mDlNANH1fYrVfjC7 .node ellipse,#mermaid-svg-mDlNANH1fYrVfjC7 .node polygon,#mermaid-svg-mDlNANH1fYrVfjC7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mDlNANH1fYrVfjC7 .rough-node .label text,#mermaid-svg-mDlNANH1fYrVfjC7 .node .label text,#mermaid-svg-mDlNANH1fYrVfjC7 .image-shape .label,#mermaid-svg-mDlNANH1fYrVfjC7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-mDlNANH1fYrVfjC7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mDlNANH1fYrVfjC7 .rough-node .label,#mermaid-svg-mDlNANH1fYrVfjC7 .node .label,#mermaid-svg-mDlNANH1fYrVfjC7 .image-shape .label,#mermaid-svg-mDlNANH1fYrVfjC7 .icon-shape .label{text-align:center;}#mermaid-svg-mDlNANH1fYrVfjC7 .node.clickable{cursor:pointer;}#mermaid-svg-mDlNANH1fYrVfjC7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mDlNANH1fYrVfjC7 .arrowheadPath{fill:#333333;}#mermaid-svg-mDlNANH1fYrVfjC7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mDlNANH1fYrVfjC7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mDlNANH1fYrVfjC7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mDlNANH1fYrVfjC7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mDlNANH1fYrVfjC7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mDlNANH1fYrVfjC7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster text{fill:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 .cluster span{color:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mDlNANH1fYrVfjC7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mDlNANH1fYrVfjC7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-mDlNANH1fYrVfjC7 .icon-shape,#mermaid-svg-mDlNANH1fYrVfjC7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mDlNANH1fYrVfjC7 .icon-shape p,#mermaid-svg-mDlNANH1fYrVfjC7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mDlNANH1fYrVfjC7 .icon-shape .label rect,#mermaid-svg-mDlNANH1fYrVfjC7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mDlNANH1fYrVfjC7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mDlNANH1fYrVfjC7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mDlNANH1fYrVfjC7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
否,不扩容
第2~10次 add
ensureCapacityInternal(n)
elementData 是

默认空数组?
calculateCapacity

返回 minCapacity
minCapacity >

数组长度 10?
直接放入元素
... 重复到第10次
容量 10, size 10

第 2 到第 10 次 add,每次需要的容量(2, 3, 4, ..., 10)都不超过当前数组长度 10,所以一次都不扩容。10 次 add 总共扩容 1 次。

阶段三:第 11 次 add(触发扩容)
#mermaid-svg-1Ea6emRyECoRptC1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1Ea6emRyECoRptC1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1Ea6emRyECoRptC1 .error-icon{fill:#552222;}#mermaid-svg-1Ea6emRyECoRptC1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1Ea6emRyECoRptC1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1Ea6emRyECoRptC1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1Ea6emRyECoRptC1 .marker.cross{stroke:#333333;}#mermaid-svg-1Ea6emRyECoRptC1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1Ea6emRyECoRptC1 p{margin:0;}#mermaid-svg-1Ea6emRyECoRptC1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1Ea6emRyECoRptC1 .cluster-label text{fill:#333;}#mermaid-svg-1Ea6emRyECoRptC1 .cluster-label span{color:#333;}#mermaid-svg-1Ea6emRyECoRptC1 .cluster-label span p{background-color:transparent;}#mermaid-svg-1Ea6emRyECoRptC1 .label text,#mermaid-svg-1Ea6emRyECoRptC1 span{fill:#333;color:#333;}#mermaid-svg-1Ea6emRyECoRptC1 .node rect,#mermaid-svg-1Ea6emRyECoRptC1 .node circle,#mermaid-svg-1Ea6emRyECoRptC1 .node ellipse,#mermaid-svg-1Ea6emRyECoRptC1 .node polygon,#mermaid-svg-1Ea6emRyECoRptC1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1Ea6emRyECoRptC1 .rough-node .label text,#mermaid-svg-1Ea6emRyECoRptC1 .node .label text,#mermaid-svg-1Ea6emRyECoRptC1 .image-shape .label,#mermaid-svg-1Ea6emRyECoRptC1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-1Ea6emRyECoRptC1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1Ea6emRyECoRptC1 .rough-node .label,#mermaid-svg-1Ea6emRyECoRptC1 .node .label,#mermaid-svg-1Ea6emRyECoRptC1 .image-shape .label,#mermaid-svg-1Ea6emRyECoRptC1 .icon-shape .label{text-align:center;}#mermaid-svg-1Ea6emRyECoRptC1 .node.clickable{cursor:pointer;}#mermaid-svg-1Ea6emRyECoRptC1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1Ea6emRyECoRptC1 .arrowheadPath{fill:#333333;}#mermaid-svg-1Ea6emRyECoRptC1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1Ea6emRyECoRptC1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1Ea6emRyECoRptC1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1Ea6emRyECoRptC1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1Ea6emRyECoRptC1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1Ea6emRyECoRptC1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1Ea6emRyECoRptC1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1Ea6emRyECoRptC1 .cluster text{fill:#333;}#mermaid-svg-1Ea6emRyECoRptC1 .cluster span{color:#333;}#mermaid-svg-1Ea6emRyECoRptC1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1Ea6emRyECoRptC1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1Ea6emRyECoRptC1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-1Ea6emRyECoRptC1 .icon-shape,#mermaid-svg-1Ea6emRyECoRptC1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1Ea6emRyECoRptC1 .icon-shape p,#mermaid-svg-1Ea6emRyECoRptC1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1Ea6emRyECoRptC1 .icon-shape .label rect,#mermaid-svg-1Ea6emRyECoRptC1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1Ea6emRyECoRptC1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1Ea6emRyECoRptC1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1Ea6emRyECoRptC1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是,需扩容
第11次 add
ensureCapacityInternal(11)
calculateCapacity

返回 11
11 > 数组长度 10?
grow()

oldCap=10 → newCap=15
Arrays.copyOf

拷贝到新数组
容量 15, size 11

size 从 10 变成 11,超过了当前容量 10,触发扩容。10 + (10 >> 1) = 15,新容量变成 15。

把三个阶段串起来:

阶段 add 次数 触发操作 扩容后容量
阶段一 第 1 次 首次初始化扩容 10
阶段二 第 2~10 次 不扩容,直接放入 10
阶段三 第 11 次 容量不够,扩容 15

再往后,第 16 次 add 会再次扩容到 22(15 + 7),以此类推。每次都是容量不够了才扩,扩完是原来的 1.5 倍。

扩容的边界处理:hugeCapacity

grow 方法里还有一个细节容易被忽略:

java 复制代码
if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);

MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8。数组在 JVM 里有一些 header 开销,所以实际能分配的最大数组长度不是 Integer.MAX_VALUE,而是减掉 8 字节的 header。

当新容量超过这个阈值时,会调用 hugeCapacity

java 复制代码
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // 溢出,说明 minCapacity 已经超过了 Integer.MAX_VALUE
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

简单说就是:正常情况下扩容到 MAX_ARRAY_SIZE 就封顶;极端情况下(minCapacity 在 MAX_ARRAY_SIZE 和 Integer.MAX_VALUE 之间)允许扩到 Integer.MAX_VALUE;真的超了 Integer.MAX_VALUE 就抛 OutOfMemoryError。

所以如果预先知道要存多少数据,最好在构造时就指定初始容量,避免反复扩容和数组拷贝。

new ArrayList(10) 扩容了几次?

这是一个经典面试题。

java 复制代码
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];  // 直接创建容量为 10 的数组
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException(...);
    }
}

带参数的构造函数直接 new Object[10],创建出来容量就是 10,存了数组就知道自己有多大。add 元素时,只有 size 超过 10 之后才会触发第一次扩容。

所以答案是:0 次。new ArrayList(10) 只是声明和实例化了一个 ArrayList,指定了容量为 10,没有发生扩容。 等你 add 第 11 个元素时才会触发第一次扩容(10 × 1.5 = 15)。

new ArrayList() 的区别:
#mermaid-svg-fh5Wn4xHwRrqtjEL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fh5Wn4xHwRrqtjEL .error-icon{fill:#552222;}#mermaid-svg-fh5Wn4xHwRrqtjEL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fh5Wn4xHwRrqtjEL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .marker.cross{stroke:#333333;}#mermaid-svg-fh5Wn4xHwRrqtjEL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fh5Wn4xHwRrqtjEL p{margin:0;}#mermaid-svg-fh5Wn4xHwRrqtjEL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster-label text{fill:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster-label span{color:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster-label span p{background-color:transparent;}#mermaid-svg-fh5Wn4xHwRrqtjEL .label text,#mermaid-svg-fh5Wn4xHwRrqtjEL span{fill:#333;color:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .node rect,#mermaid-svg-fh5Wn4xHwRrqtjEL .node circle,#mermaid-svg-fh5Wn4xHwRrqtjEL .node ellipse,#mermaid-svg-fh5Wn4xHwRrqtjEL .node polygon,#mermaid-svg-fh5Wn4xHwRrqtjEL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .rough-node .label text,#mermaid-svg-fh5Wn4xHwRrqtjEL .node .label text,#mermaid-svg-fh5Wn4xHwRrqtjEL .image-shape .label,#mermaid-svg-fh5Wn4xHwRrqtjEL .icon-shape .label{text-anchor:middle;}#mermaid-svg-fh5Wn4xHwRrqtjEL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .rough-node .label,#mermaid-svg-fh5Wn4xHwRrqtjEL .node .label,#mermaid-svg-fh5Wn4xHwRrqtjEL .image-shape .label,#mermaid-svg-fh5Wn4xHwRrqtjEL .icon-shape .label{text-align:center;}#mermaid-svg-fh5Wn4xHwRrqtjEL .node.clickable{cursor:pointer;}#mermaid-svg-fh5Wn4xHwRrqtjEL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .arrowheadPath{fill:#333333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fh5Wn4xHwRrqtjEL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fh5Wn4xHwRrqtjEL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fh5Wn4xHwRrqtjEL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster text{fill:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL .cluster span{color:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fh5Wn4xHwRrqtjEL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fh5Wn4xHwRrqtjEL rect.text{fill:none;stroke-width:0;}#mermaid-svg-fh5Wn4xHwRrqtjEL .icon-shape,#mermaid-svg-fh5Wn4xHwRrqtjEL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fh5Wn4xHwRrqtjEL .icon-shape p,#mermaid-svg-fh5Wn4xHwRrqtjEL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fh5Wn4xHwRrqtjEL .icon-shape .label rect,#mermaid-svg-fh5Wn4xHwRrqtjEL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fh5Wn4xHwRrqtjEL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fh5Wn4xHwRrqtjEL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fh5Wn4xHwRrqtjEL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} new ArrayList()
elementData 指向空数组
第 1 次 add
扩容 1:从 0 到 10
第 11 次 add
扩容 2:从 10 到 15
new ArrayList(10)
elementData 已经是长度为 10 的数组
前 10 次 add,不扩容
第 11 次 add
扩容 1:从 10 到 15

数组与 List 互转

数组转 List:Arrays.asList

java 复制代码
String[] strs = {"aaa", "bbb", "ccc"};
List<String> list = Arrays.asList(strs);

这个方法返回的不是 java.util.ArrayList,而是 Arrays 内部的一个私有静态类 ArrayList。它底层直接引用原数组,没有拷贝数据。

java 复制代码
// 修改数组的内容
strs[1] = "ddd";
System.out.println(list);  // [aaa, ddd, ccc] ------ list 也变了!

因为底层用的是同一个数组,所以修改数组,List 会受影响

注意:Arrays.asList 返回的 List 不支持 add/remove 操作。想得到一个真正独立的 ArrayList,应该这样写:

java 复制代码
List<String> list = new ArrayList<>(Arrays.asList(strs));

List 转数组:toArray

java 复制代码
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

// 方式一:无参,返回 Object[]
Object[] arr1 = list.toArray();

// 方式二:指定类型,更常用
String[] arr2 = list.toArray(new String[0]);

无参 toArray 返回 Object 数组,需要自己强转。推荐用带参版本,直接拿到正确的类型。

关键点:toArray 底层做了数组拷贝,返回的是一个全新的数组。

java 复制代码
String[] array = list.toArray(new String[list.size()]);
list.add("ddd");
System.out.println(Arrays.toString(array));  // [aaa, bbb, ccc] ------ 不受影响

List 修改之后,toArray 生成的数组不受影响,因为底层拷贝了一份。
#mermaid-svg-KAUU3WF9jU5MWKUk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KAUU3WF9jU5MWKUk .error-icon{fill:#552222;}#mermaid-svg-KAUU3WF9jU5MWKUk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KAUU3WF9jU5MWKUk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KAUU3WF9jU5MWKUk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KAUU3WF9jU5MWKUk .marker.cross{stroke:#333333;}#mermaid-svg-KAUU3WF9jU5MWKUk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KAUU3WF9jU5MWKUk p{margin:0;}#mermaid-svg-KAUU3WF9jU5MWKUk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster-label text{fill:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster-label span{color:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster-label span p{background-color:transparent;}#mermaid-svg-KAUU3WF9jU5MWKUk .label text,#mermaid-svg-KAUU3WF9jU5MWKUk span{fill:#333;color:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk .node rect,#mermaid-svg-KAUU3WF9jU5MWKUk .node circle,#mermaid-svg-KAUU3WF9jU5MWKUk .node ellipse,#mermaid-svg-KAUU3WF9jU5MWKUk .node polygon,#mermaid-svg-KAUU3WF9jU5MWKUk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KAUU3WF9jU5MWKUk .rough-node .label text,#mermaid-svg-KAUU3WF9jU5MWKUk .node .label text,#mermaid-svg-KAUU3WF9jU5MWKUk .image-shape .label,#mermaid-svg-KAUU3WF9jU5MWKUk .icon-shape .label{text-anchor:middle;}#mermaid-svg-KAUU3WF9jU5MWKUk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KAUU3WF9jU5MWKUk .rough-node .label,#mermaid-svg-KAUU3WF9jU5MWKUk .node .label,#mermaid-svg-KAUU3WF9jU5MWKUk .image-shape .label,#mermaid-svg-KAUU3WF9jU5MWKUk .icon-shape .label{text-align:center;}#mermaid-svg-KAUU3WF9jU5MWKUk .node.clickable{cursor:pointer;}#mermaid-svg-KAUU3WF9jU5MWKUk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KAUU3WF9jU5MWKUk .arrowheadPath{fill:#333333;}#mermaid-svg-KAUU3WF9jU5MWKUk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KAUU3WF9jU5MWKUk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KAUU3WF9jU5MWKUk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KAUU3WF9jU5MWKUk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KAUU3WF9jU5MWKUk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KAUU3WF9jU5MWKUk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster text{fill:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk .cluster span{color:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KAUU3WF9jU5MWKUk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KAUU3WF9jU5MWKUk rect.text{fill:none;stroke-width:0;}#mermaid-svg-KAUU3WF9jU5MWKUk .icon-shape,#mermaid-svg-KAUU3WF9jU5MWKUk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KAUU3WF9jU5MWKUk .icon-shape p,#mermaid-svg-KAUU3WF9jU5MWKUk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KAUU3WF9jU5MWKUk .icon-shape .label rect,#mermaid-svg-KAUU3WF9jU5MWKUk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KAUU3WF9jU5MWKUk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KAUU3WF9jU5MWKUk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KAUU3WF9jU5MWKUk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} List转数组
List
list.toArray()
底层 Arrays.copyOf

拷贝出新数组
修改 List → 数组不受影响
数组转List
String\[\] strs
Arrays.asList(strs)
内部类 ArrayList

直接引用原数组
修改原数组 → List 受影响

ArrayList 是线程不安全的

ArrayList 的方法没加 synchronized,多线程并发 add 会出问题。

典型场景:两个线程同时 add,都读到 size = 5,都往 elementData5 写数据,结果一个元素被覆盖。或者扩容时,线程 A 正在 Arrays.copyOf,线程 B 读到一半拷贝了一半没拷贝的数组,直接崩。

需要线程安全的 List 有两种方案:

java 复制代码
// 方案一:Collections.synchronizedList 包装
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

// 方案二:直接用 CopyOnWriteArrayList
List<String> cowList = new CopyOnWriteArrayList<>();

synchronizedList 是给所有方法加锁,写操作性能一般。CopyOnWriteArrayList 是写时复制,适合读多写少的场景。

面试模板

如果面试官问"说一下 ArrayList 的底层实现原理",可以这样回答:

ArrayList 底层是用动态数组实现的。成员变量 elementData 是 Object 数组,size 记录实际元素个数。

无参构造时,并没有直接创建数组,而是把 elementData 指向一个共享的空数组实例。第一次 add 时,才会把容量初始化为默认的 10。

add 方法先调用 ensureCapacityInternal 检查容量是否够用,不够就调 grow 扩容。扩容大小是原来容量的 1.5 倍,通过 oldCapacity + (oldCapacity >> 1) 计算,底层用 Arrays.copyOf 把老数据拷贝到新数组。

如果构造时指定了初始容量,比如 new ArrayList(10),就直接创建长度为 10 的数组,在存满之前不需要扩容。数组转 List 用 Arrays.asList,它底层直接引用原数组,互相修改会影响。List 转数组用 toArray,底层做了拷贝,所以互不影响。

ArrayList 不是线程安全的,多线程场景可以用 Collections.synchronizedList 包装或者用 CopyOnWriteArrayList。

相关推荐
Java程序员-小白1 小时前
Spring Boot整合Sa-Token框架(入门篇)
java·spring boot·后端·sa-token
NE_STOP1 小时前
Docker--初识Dockerfile
java
码哥字节2 小时前
升到 Spring Boot 4.1,虚拟线程开了,HikariCP 连接池却崩了
java·springboot·claude code
devilnumber2 小时前
java自定义事件处理器极简版:「外卖点餐」场景
java·开发语言
J2虾虾2 小时前
Spring AI Alibaba - 智能体作为工具(Agent Tool)
java·人工智能·spring
Hesionberger2 小时前
巧用异或找出唯一数字(多解)
java·数据结构·python·算法·leetcode
铁链鞭策大师2 小时前
javaEE之多线程(2)
java·前端·java-ee
Devin~Y2 小时前
从内容社区到AIGC客服:Spring Boot、Redis、Kafka、K8s、RAG的三轮大厂Java面试对话(附标准答案)
java·spring boot·redis·spring cloud·kafka·kubernetes·micrometer