上一篇讲了数组和链表的基础概念,这篇直接进入正题------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_ELEMENTDATA,calculateCapacity 直接返回 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。