ArrayList 和 LinkedList 的区别?一篇讲透,从此开发和面试都不再纠结

在日常Java开发中,我们几乎每天都在使用List 。

而Java中最常用的两个列表实现就是 ArrayListLinkedList。面试官也是,非常喜欢问它们有什么区别。

很多朋友会死记硬背:"ArrayList 查询快,增删慢; LinkedList 查询慢,增删快。"

但你知道为什么吗?今天我们来深入的了解一下。


一、举个例子

你有一排书架:

ArrayList 就像一整排连续的书柜

所有书按顺序紧挨着放,编号 0、1、2......你想拿第5本书时,直接走过去拿就行,非常快!

LinkedList 就像用绳子串连起来的一堆独立小盒子

每个盒子里除了书,还写着前一个盒子在哪和后一个盒子在哪。你想拿第5本书时,得从第一个盒子开始,一个一个顺着绳子找过去。

下面我们再从技术角度进行深入分析。


二、底层结构:数组 vs 链表

ArrayList

ArrayList 底层是动态数组,元素在内存中是连续存放的。

java 复制代码
// ArrayList简化版原理
public class ArrayList {
    private Object[] elementData; // 核心数组
    private int size; // 当前元素个数
    
    // 添加元素时,如果数组满了会自动扩容
    public void add(Object element) {
        if (size == elementData.length) {
            // 创建一个1.5倍大的新数组,然后拷贝数据
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        elementData[size++] = element;
    }
}

特点:

  • 内存中连续存储,像一排整齐的座位
  • 自动扩容,但扩容时需要拷贝所有数据
  • 访问速度快,但插入删除可能较慢

LinkedList

LinkedList 的底层是双向链表,元素在内存中是分散存放的,每个元素(节点)都记录着它前后邻居的"地址"。

java 复制代码
// LinkedList节点结构
class Node {
    E item;        // 当前节点数据
    Node next;     // 指向下一个节点
    Node prev;     // 指向上一个节点
}

// LinkedList简化版原理
public class LinkedList {
    private Node first; // 头节点
    private Node last;  // 尾节点
    private int size;
}

每个元素(节点)都保存着前后邻居的信息,就像每个人都知道自己前后是谁。


三、性能对比

我们从三个最常用的操作来看:

1. 随机访问(比如 list.get(100))

ArrayList:O(1)

直接跳到第 100 个位置,秒取!

LinkedList:O(n)

得从头开始数 100 次才能拿到。

结论 :如果你经常通过下标取数据(比如 for 循环遍历),ArrayList要快得多!


2. 在末尾添加/删除(list.add(obj) 或 remove(last))

ArrayList:平均 O(1)

正常情况直接加在最后;但如果空间满了,要扩容(复制整个数组),这时会慢一下(但平均下来还是很快)。

LinkedList:O(1)

直接在尾巴上挂新节点,永远不慢。

结论 :两者在尾部操作都很快,但 ArrayList 还是占优势(因为内存连续,CPU缓存友好)。


3. 在中间或开头插入/删除(比如 list.add(0, obj))

ArrayList:O(n)

插入第 0 位?后面所有元素都要往后挪一位!删也一样,前面空了,后面的要往前挤。

LinkedList:理论上 O(1),但实际常是 O(n)

虽然插入本身只要改指针,但你要先找到第 0 个节点啊!找的过程就要从头遍历,所以整体还是 O(n)。

重要提醒: 很多人以为 LinkedList 在中间插入更快,其实只有当你已经有那个位置的引用时才快(比如用 Iterator 遍历时删除)。

如果只是用 list.add(500, obj),它照样要先找第 500 个节点,速度并不比 ArrayList 快!


四、内存占用

ArrayList

只存数据和一点预留空间(比如容量 10,只用了 6,那有 4 个空位)。

LinkedList

每个元素都要额外存两个指针(指向前一个和后一个节点),内存开销大很多

举个例子:存1000个 Integer 对象:

  • ArrayList:约4KB(假设每个 int 4 字节)
  • LinkedList:约4KB(数据)+ 8KB(指针,每个指针 8 字节 × 2 × 1000)= 12KB+

结论ArrayList 更省内存,尤其存大量小对象时优势更加明显。


五、其他细节补充

是否支持"快速随机访问"?

  • ArrayList 实现了 RandomAccess 接口(这是一个标记接口,告诉程序员:"我支持快速随机访问")。
  • LinkedList 没有实现它。

有些工具类(如 Collections.binarySearch)会根据这个接口选择不同算法,进一步优化性能。

线程安全吗?

都不安全! 多线程环境下直接用会出问题。

如果需要线程安全,可以用:

java 复制代码
List<String> safeList = Collections.synchronizedList(new ArrayList<>());

或者用 CopyOnWriteArrayList(适合读多写少的场景)。

LinkedList 还能当队列用!

LinkedList 实现了 Deque 接口,可以当双端队列用:

java 复制代码
LinkedList<String> queue = new LinkedList<>();
queue.addFirst("A");
queue.addLast("B");
String first = queue.removeFirst(); // A

这时候它的链表结构就非常合适!


六、开发时该用哪个?

使用场景 推荐选择 原因
经常通过下标访问(如 for 循环、get(i)) ArrayList 随机访问快
主要在末尾增删(如日志记录、追加数据) ArrayList 简单高效,缓存友好
需要频繁在已知位置插入/删除(如用 Iterator 遍历时删元素) LinkedList 指针操作快
要实现队列、栈、双端队列 LinkedList 原生支持 addFirst/removeLast
内存紧张 or 存大量小对象 ArrayList 内存更紧凑

一般情况下,默认选 ArrayList 就对了!

只有在明确需要链表特性(如双端操作)时,才考虑 LinkedList。


总结

ArrayList 底层是数组,内存连续,访问快(O(1)),中间增删慢(O(n))。

LinkedList 底层是链表,内存分散,访问慢(O(n)),头尾增删快(O(1))。

日常开发中,优先选 ArrayList 准没错!

只有当你真的需要频繁在列表头尾增删、或者用它当队列/栈时,再考虑 LinkedList

本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《代码里全是 new 对象,真的很 Low 吗?我认真想了一晚》

《Java 开发必看:什么时候用 for,什么时候用 Stream?》

《这 5 个冷门 HTML 标签,让我直接删了100 行 JS 代码》

《Vue 组件通信的 8 种最佳实践,你知道几种?》

相关推荐
码出财富1 天前
SpringBoot 内置的 20 个高效工具类
java·spring boot·spring cloud·java-ee
我是小疯子661 天前
Python变量赋值陷阱:浅拷贝VS深拷贝
java·服务器·数据库
森叶1 天前
Java 比 Python 高性能的原因:重点在高并发方面
java·开发语言·python
二哈喇子!1 天前
Eclipse中导入外部jar包
java·eclipse·jar
微露清风1 天前
系统性学习C++-第二十二讲-C++11
java·c++·学习
进阶小白猿1 天前
Java技术八股学习Day20
java·开发语言·学习
gis开发1 天前
【无标题】
java·前端·javascript
Wpa.wk1 天前
性能测试 - 搭建线上的性能测试环境参考逻辑图
java·经验分享·测试工具·jmeter·性能测试
代码村新手1 天前
C++-类和对象(中)
java·开发语言·c++
葵花楹1 天前
【JAVA课设】【游戏社交系统】
java·开发语言·游戏