ArrayList vs LinkedList:底层原理、性能对决与扩容机制全解析

ArrayList vs LinkedList:底层原理、性能对决与扩容机制全解析

1. 引言

在 Java 开发中,ArrayListLinkedList 是最常用的两种 List 实现。面试官常常抛出这样一个问题:"ArrayList 和 LinkedList 有什么区别?" 如果你只回答"数组 vs 链表,查询快增删慢",显然不够深入。真正的理解需要结合底层数据结构、内存布局、时间复杂度以及实际场景的选型。

本文将从源码级别剖析两者差异,并详细解读 ArrayList 的动态扩容机制(包括 1.5 倍扩容的由来),配合流程图和性能对比表,让你彻底掌握这两个集合类的本质。


2. 架构概览(UML 类图)

#mermaid-svg-jNa9K55XMFQFUqiV{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-jNa9K55XMFQFUqiV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jNa9K55XMFQFUqiV .error-icon{fill:#552222;}#mermaid-svg-jNa9K55XMFQFUqiV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jNa9K55XMFQFUqiV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jNa9K55XMFQFUqiV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jNa9K55XMFQFUqiV .marker.cross{stroke:#333333;}#mermaid-svg-jNa9K55XMFQFUqiV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jNa9K55XMFQFUqiV p{margin:0;}#mermaid-svg-jNa9K55XMFQFUqiV g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-jNa9K55XMFQFUqiV g.classGroup text .title{font-weight:bolder;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster-label text{fill:#333;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster-label span{color:#333;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster-label span p{background-color:transparent;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster text{fill:#333;}#mermaid-svg-jNa9K55XMFQFUqiV .cluster span{color:#333;}#mermaid-svg-jNa9K55XMFQFUqiV .nodeLabel,#mermaid-svg-jNa9K55XMFQFUqiV .edgeLabel{color:#131300;}#mermaid-svg-jNa9K55XMFQFUqiV .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-jNa9K55XMFQFUqiV .label text{fill:#131300;}#mermaid-svg-jNa9K55XMFQFUqiV .labelBkg{background:#ECECFF;}#mermaid-svg-jNa9K55XMFQFUqiV .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-jNa9K55XMFQFUqiV .classTitle{font-weight:bolder;}#mermaid-svg-jNa9K55XMFQFUqiV .node rect,#mermaid-svg-jNa9K55XMFQFUqiV .node circle,#mermaid-svg-jNa9K55XMFQFUqiV .node ellipse,#mermaid-svg-jNa9K55XMFQFUqiV .node polygon,#mermaid-svg-jNa9K55XMFQFUqiV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jNa9K55XMFQFUqiV .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV g.clickable{cursor:pointer;}#mermaid-svg-jNa9K55XMFQFUqiV g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-jNa9K55XMFQFUqiV g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-jNa9K55XMFQFUqiV .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-jNa9K55XMFQFUqiV .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-jNa9K55XMFQFUqiV .dashed-line{stroke-dasharray:3;}#mermaid-svg-jNa9K55XMFQFUqiV .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-jNa9K55XMFQFUqiV #compositionStart,#mermaid-svg-jNa9K55XMFQFUqiV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #compositionEnd,#mermaid-svg-jNa9K55XMFQFUqiV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #dependencyStart,#mermaid-svg-jNa9K55XMFQFUqiV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #dependencyStart,#mermaid-svg-jNa9K55XMFQFUqiV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #extensionStart,#mermaid-svg-jNa9K55XMFQFUqiV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #extensionEnd,#mermaid-svg-jNa9K55XMFQFUqiV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #aggregationStart,#mermaid-svg-jNa9K55XMFQFUqiV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #aggregationEnd,#mermaid-svg-jNa9K55XMFQFUqiV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #lollipopStart,#mermaid-svg-jNa9K55XMFQFUqiV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV #lollipopEnd,#mermaid-svg-jNa9K55XMFQFUqiV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-jNa9K55XMFQFUqiV .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-jNa9K55XMFQFUqiV .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jNa9K55XMFQFUqiV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jNa9K55XMFQFUqiV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jNa9K55XMFQFUqiV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
List
+add()
+remove()
+get(index)
ArrayList
-Object\[\] elementData
-int size
+ensureCapacity()
+grow()
LinkedList
-Node first
-Node last
-int size
+linkFirst()
+linkLast()
AbstractList
AbstractSequentialList
Node
-E item
-Node prev
-Node next

ArrayList 基于数组实现,LinkedList 基于双向链表实现。两者都实现了 List 接口,但内部结构决定了它们截然不同的性能特征。


3. ArrayList 与 LinkedList 核心区别

对比维度 ArrayList LinkedList
底层数据结构 动态数组(Object\[\]) 双向链表(Node 节点)
随机访问(get/set) O(1) 极快 O(n) 需要遍历
插入/删除 尾部 O(1)(无需移动),中间 O(n)(需要移动元素) 头尾 O(1),中间 O(n)(需定位到位置)
内存占用 数组有预留空间(可能浪费),但每个元素只存引用 每个节点额外存储两个指针(prev/next),内存开销更大
线程安全 非线程安全(需外部同步) 非线程安全
迭代性能 连续内存,CPU 缓存友好 节点分散,缓存不友好
适用场景 随机访问多、尾部插入多 频繁头部/尾部操作、中间插入删除少

3.1 详细解读

3.1.1 为什么 ArrayList 查询快?

ArrayList 底层是一个连续的内存数组。通过索引访问元素时,只需计算 baseAddress + index * elementSize,直接定位到内存地址,时间复杂度 O(1)。而 LinkedList 需要通过指针逐个遍历,即使获取中间元素也要从头部或尾部开始移动,复杂度 O(n)。

3.1.2 为什么 LinkedList 增删快?(特指头部/尾部)

对于 LinkedList,在头部或尾部添加/删除元素只需修改几个指针引用,时间复杂度 O(1)。但在中间位置插入或删除,仍然需要先遍历到该位置(O(n)),然后修改指针,总体仍是 O(n)。相比之下,ArrayList 在中间插入/删除需要移动后续所有元素,成本极高。

java 复制代码
// ArrayList 中间插入源码示意
public void add(int index, E element) {
    // 检查越界
    // 扩容
    // 使用 System.arraycopy 移动元素
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}
3.1.3 性能测试(参考数据)
操作 ArrayList (10w条) LinkedList (10w条)
尾部添加 ~2ms ~3ms
头部添加 ~50ms ~2ms
中间添加 ~35ms ~20ms(定位+插入)
随机访问 ~1ms ~150ms
迭代 较慢(节点跳跃)

结论 :日常开发中 90% 的场景使用 ArrayList 即可;只有需要频繁在头部或尾部插入/删除(如实现队列、栈)时,才考虑 LinkedList


4. ArrayList 自动扩容机制深度剖析

ArrayList 的底层数组长度是固定的,当添加的元素超过当前数组容量时,必须进行扩容。理解扩容机制对于优化内存和性能至关重要。

4.1 扩容流程

#mermaid-svg-d4kumVRq0uCtKyGO{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-d4kumVRq0uCtKyGO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d4kumVRq0uCtKyGO .error-icon{fill:#552222;}#mermaid-svg-d4kumVRq0uCtKyGO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d4kumVRq0uCtKyGO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d4kumVRq0uCtKyGO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d4kumVRq0uCtKyGO .marker.cross{stroke:#333333;}#mermaid-svg-d4kumVRq0uCtKyGO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d4kumVRq0uCtKyGO p{margin:0;}#mermaid-svg-d4kumVRq0uCtKyGO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-d4kumVRq0uCtKyGO .cluster-label text{fill:#333;}#mermaid-svg-d4kumVRq0uCtKyGO .cluster-label span{color:#333;}#mermaid-svg-d4kumVRq0uCtKyGO .cluster-label span p{background-color:transparent;}#mermaid-svg-d4kumVRq0uCtKyGO .label text,#mermaid-svg-d4kumVRq0uCtKyGO span{fill:#333;color:#333;}#mermaid-svg-d4kumVRq0uCtKyGO .node rect,#mermaid-svg-d4kumVRq0uCtKyGO .node circle,#mermaid-svg-d4kumVRq0uCtKyGO .node ellipse,#mermaid-svg-d4kumVRq0uCtKyGO .node polygon,#mermaid-svg-d4kumVRq0uCtKyGO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d4kumVRq0uCtKyGO .rough-node .label text,#mermaid-svg-d4kumVRq0uCtKyGO .node .label text,#mermaid-svg-d4kumVRq0uCtKyGO .image-shape .label,#mermaid-svg-d4kumVRq0uCtKyGO .icon-shape .label{text-anchor:middle;}#mermaid-svg-d4kumVRq0uCtKyGO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d4kumVRq0uCtKyGO .rough-node .label,#mermaid-svg-d4kumVRq0uCtKyGO .node .label,#mermaid-svg-d4kumVRq0uCtKyGO .image-shape .label,#mermaid-svg-d4kumVRq0uCtKyGO .icon-shape .label{text-align:center;}#mermaid-svg-d4kumVRq0uCtKyGO .node.clickable{cursor:pointer;}#mermaid-svg-d4kumVRq0uCtKyGO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d4kumVRq0uCtKyGO .arrowheadPath{fill:#333333;}#mermaid-svg-d4kumVRq0uCtKyGO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d4kumVRq0uCtKyGO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d4kumVRq0uCtKyGO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d4kumVRq0uCtKyGO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d4kumVRq0uCtKyGO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d4kumVRq0uCtKyGO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d4kumVRq0uCtKyGO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d4kumVRq0uCtKyGO .cluster text{fill:#333;}#mermaid-svg-d4kumVRq0uCtKyGO .cluster span{color:#333;}#mermaid-svg-d4kumVRq0uCtKyGO 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-d4kumVRq0uCtKyGO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d4kumVRq0uCtKyGO rect.text{fill:none;stroke-width:0;}#mermaid-svg-d4kumVRq0uCtKyGO .icon-shape,#mermaid-svg-d4kumVRq0uCtKyGO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d4kumVRq0uCtKyGO .icon-shape p,#mermaid-svg-d4kumVRq0uCtKyGO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d4kumVRq0uCtKyGO .icon-shape .label rect,#mermaid-svg-d4kumVRq0uCtKyGO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d4kumVRq0uCtKyGO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d4kumVRq0uCtKyGO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d4kumVRq0uCtKyGO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否



调用 add(e)
检查是否需要扩容
当前 size + 1 > 数组长度?
直接添加到尾部
调用 grow(minCapacity)
新容量 = 原容量 + 原容量 >> 1
新容量是否超过最大限制?
创建新数组,长度为新容量
使用 Integer.MAX_VALUE 或超大值
System.arraycopy 复制原数据
添加新元素
结束

4.2 扩容规则源码解读(JDK 8+)

java 复制代码
// ArrayList 中的 grow 方法
private void grow(int minCapacity) {
    // 旧容量
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 + 旧容量 >> 1(即 1.5 倍)
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新容量仍小于所需最小容量,则使用 minCapacity
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新容量超过 MAX_ARRAY_SIZE,则调用 hugeCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 复制数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}
关键点:
  • 初始容量 :无参构造时,elementData 指向一个空数组 {},第一次 add 时才会扩容到默认的 DEFAULT_CAPACITY = 10
  • 扩容倍数oldCapacity + (oldCapacity >> 1) = oldCapacity * 1.5。右移一位相当于除以 2,再加上原值得到 1.5 倍。
  • 为什么是 1.5 倍? 权衡空间与时间:太小(如 1.1 倍)会导致频繁扩容,影响性能;太大(如 2 倍)可能浪费内存。1.5 倍是一个经验值,既减少扩容次数,又不会造成过多空间浪费。

4.3 示例:扩容过程演示

假设初始容量为 10,添加第 11 个元素时触发扩容:

操作次数 当前容量 元素个数 是否扩容 新容量
1~10 10 0→10 首次 add 时从 0 扩容到 10 10
11 10 11 10 + 10>>1 = 15
16 15 16 15 + 7 = 22
23 22 23 22 + 11 = 33
... ... ... ... ...

每次扩容都会发生一次 Arrays.copyOf,这是一个 O(n) 操作,因此尽量在创建 ArrayList 时指定合适的初始容量,可避免多次扩容带来的性能损耗。

java 复制代码
// 预估需要存储 10000 个元素
List<String> list = new ArrayList<>(10000);

4.4 容量相关方法

  • ensureCapacity(int minCapacity):手动预分配容量(提前扩容)。
  • trimToSize():将数组容量收缩到当前实际元素个数,释放多余内存(适合大量添加后不再增加的情况)。

5. 对比总结与选型建议

5.1 决策流程图

#mermaid-svg-VIKpTVpynF1dGQUz{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-VIKpTVpynF1dGQUz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VIKpTVpynF1dGQUz .error-icon{fill:#552222;}#mermaid-svg-VIKpTVpynF1dGQUz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VIKpTVpynF1dGQUz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VIKpTVpynF1dGQUz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VIKpTVpynF1dGQUz .marker.cross{stroke:#333333;}#mermaid-svg-VIKpTVpynF1dGQUz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VIKpTVpynF1dGQUz p{margin:0;}#mermaid-svg-VIKpTVpynF1dGQUz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VIKpTVpynF1dGQUz .cluster-label text{fill:#333;}#mermaid-svg-VIKpTVpynF1dGQUz .cluster-label span{color:#333;}#mermaid-svg-VIKpTVpynF1dGQUz .cluster-label span p{background-color:transparent;}#mermaid-svg-VIKpTVpynF1dGQUz .label text,#mermaid-svg-VIKpTVpynF1dGQUz span{fill:#333;color:#333;}#mermaid-svg-VIKpTVpynF1dGQUz .node rect,#mermaid-svg-VIKpTVpynF1dGQUz .node circle,#mermaid-svg-VIKpTVpynF1dGQUz .node ellipse,#mermaid-svg-VIKpTVpynF1dGQUz .node polygon,#mermaid-svg-VIKpTVpynF1dGQUz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VIKpTVpynF1dGQUz .rough-node .label text,#mermaid-svg-VIKpTVpynF1dGQUz .node .label text,#mermaid-svg-VIKpTVpynF1dGQUz .image-shape .label,#mermaid-svg-VIKpTVpynF1dGQUz .icon-shape .label{text-anchor:middle;}#mermaid-svg-VIKpTVpynF1dGQUz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VIKpTVpynF1dGQUz .rough-node .label,#mermaid-svg-VIKpTVpynF1dGQUz .node .label,#mermaid-svg-VIKpTVpynF1dGQUz .image-shape .label,#mermaid-svg-VIKpTVpynF1dGQUz .icon-shape .label{text-align:center;}#mermaid-svg-VIKpTVpynF1dGQUz .node.clickable{cursor:pointer;}#mermaid-svg-VIKpTVpynF1dGQUz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VIKpTVpynF1dGQUz .arrowheadPath{fill:#333333;}#mermaid-svg-VIKpTVpynF1dGQUz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VIKpTVpynF1dGQUz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VIKpTVpynF1dGQUz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VIKpTVpynF1dGQUz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VIKpTVpynF1dGQUz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VIKpTVpynF1dGQUz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VIKpTVpynF1dGQUz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VIKpTVpynF1dGQUz .cluster text{fill:#333;}#mermaid-svg-VIKpTVpynF1dGQUz .cluster span{color:#333;}#mermaid-svg-VIKpTVpynF1dGQUz 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-VIKpTVpynF1dGQUz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VIKpTVpynF1dGQUz rect.text{fill:none;stroke-width:0;}#mermaid-svg-VIKpTVpynF1dGQUz .icon-shape,#mermaid-svg-VIKpTVpynF1dGQUz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VIKpTVpynF1dGQUz .icon-shape p,#mermaid-svg-VIKpTVpynF1dGQUz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VIKpTVpynF1dGQUz .icon-shape .label rect,#mermaid-svg-VIKpTVpynF1dGQUz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VIKpTVpynF1dGQUz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VIKpTVpynF1dGQUz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VIKpTVpynF1dGQUz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





需要使用 List
是否频繁随机访问?
使用 ArrayList
是否主要在头部/尾部操作?
使用 LinkedList
是否需要频繁在中间插入删除?
两者差异不大,均需 O(n) 定位
建议 ArrayList,内存更小

5.2 性能黄金法则

  • 读多写少ArrayList
  • 队列/栈(头尾操作)LinkedList(或 ArrayDeque 更优)
  • 无法预估数据量且频繁追加ArrayList 并指定初始容量
  • 需要线程安全Vector(已过时)或 Collections.synchronizedListCopyOnWriteArrayList

5.3 特别注意

  • LinkedList 并不适合所有"增删快"场景:中间插入仍需遍历,且内存开销大,在多数实际业务中 ArrayList 表现更好。
  • ArrayList 的扩容是一个代价较高的操作,大数据量下务必预分配容量。

6. 常见面试题

Q1: ArrayList 初始容量是 10 吗?

答:无参构造时,初始容量为 0,第一次 add 时才扩容到 10。可以通过 new ArrayList(10) 直接指定初始容量。

Q2: 为什么 ArrayList 扩容是 1.5 倍,而不是 2 倍?

答:1.5 倍是时间与空间的折中。过大的扩容因子(如 2 倍)会导致内存浪费;过小的因子(如 1.1 倍)会导致频繁扩容。1.5 倍经过实践检验,性能较好。

Q3: LinkedList 实现了 Deque 接口,能否作为双端队列使用?

答:可以。LinkedList 实现了 Deque,提供 addFirstaddLastpollFirstpollLast 等方法。但如果是纯队列场景,ArrayDeque 性能通常更优(基于循环数组,无节点开销)。

Q4: ArrayList 的 System.arraycopyArrays.copyOf 有什么区别?

答:System.arraycopy 是 native 方法,直接复制内存区域;Arrays.copyOf 内部调用了 System.arraycopy,但会先创建新数组。两者都是高效的浅拷贝。


7. 总结

  • ArrayList:基于数组,随机访问 O(1),中间插入删除 O(n),扩容 1.5 倍。
  • LinkedList:基于双向链表,头尾操作 O(1),随机访问 O(n),内存开销大。
  • 扩容机制 :首次添加时扩容到 10,之后每次扩容为当前的 1.5 倍(oldCapacity + oldCapacity >> 1)。
  • 选型建议 :绝大多数情况选 ArrayList,只有明确需要频繁头尾操作且不需要随机访问时才考虑 LinkedList

记住一句话:"数组紧凑链表散,随机选 Array,队尾队首看 List。"

相关推荐
超梦dasgg15 小时前
归并排序 Java 实现(递归 + 非递归)
java·算法·排序算法
我是一颗柠檬15 小时前
【JDK8新特性】JDK8实战与面试高频考点汇总Day12
java·开发语言·后端·面试·职场和发展
疯狂成瘾者15 小时前
常见的优化查询速度方法
java
YOU OU15 小时前
Spring事务和事务传播机制
java·数据库·spring
Chase_______15 小时前
【Java基础】5 / 2 为什么等于 2?整数除法、取余和 floorMod 一次讲清
java·开发语言
fish_xk15 小时前
c++11(二)
java·前端·c++
星空15 小时前
跨域请求测试
java·java-ee
宠友信息15 小时前
友猫社区Vue与Spring Boot多端社交平台源码架构
java·vue.js·spring boot·架构
love8888_cnsd15 小时前
Git & Linux 速查表
java·linux·git·后端·elasticsearch