首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【集合框架List接口】

【集合框架List接口】

作者头像
艾伦耶格尔
发布2025-08-28 15:49:21
发布2025-08-28 15:49:21
1760
举报
文章被收录于专栏:Java基础Java基础

👉 用 ArrayList 存数据,结果插入时卡住了? 👉 想删除某个元素,却发现索引错乱了? 👉 不知道该用 ArrayList 还是 LinkedList,选错了导致性能瓶颈?


一、List 是什么?—— 有序可重复的“列表”

🎯 核心特点
  • 有序(Ordered):元素按插入顺序排列
  • 允许重复:可以存储多个相同的值
  • 支持索引访问:可以通过 get(index) 快速定位元素

典型场景

  • 用户列表展示
  • 购物车商品管理
  • 日志记录
🖼️ 图示:List 的逻辑结构
代码语言:javascript
复制
+--------+    +--------+    +--------+
| index0 | -> | index1 | -> | index2 | -> ...
+--------+    +--------+    +--------+
| valueA |    | valueB |    | valueC |
+--------+    +--------+    +--------+

核心操作

  • 添加元素:add(E e)
  • 获取元素:get(int index)
  • 删除元素:remove(int index)

二、List 的核心实现类

1. ArrayList —— 基于“动态数组”的实现
🎯 核心特性
  • 底层是数组Object[] elementData
  • 支持快速随机访问:通过索引直接定位,时间复杂度 O(1)
  • 动态扩容:当容量不够时,自动扩展(默认扩容 1.5 倍)
  • 非线程安全
代码语言:javascript
复制
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
String first = list.get(0); // 快速获取第一个元素
🔍 动态扩容机制
代码语言:javascript
复制
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 + 旧容量/2 (即 1.5 倍)
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 创建新数组,复制数据
    elementData = Arrays.copyOf(elementData, newCapacity);
}

内存变化

代码语言:javascript
复制
扩容前:[Apple][Banana]  (容量=2, size=2)
扩容后:[Apple][Banana][ ] (容量=3, size=2)

建议:初始化时指定合理容量,避免频繁扩容影响性能。

❌ 经典误区:中间插入/删除慢
代码语言:javascript
复制
// 错误:频繁在中间插入/删除,导致后面元素移动,性能 O(n)
list.add(1, "Orange"); // 插入到索引 1
list.remove(1);        // 删除索引 1

结论ArrayList 适合查询多、增删少的场景。


2. LinkedList —— 基于“双向链表”的实现
🎯 核心特性
  • 底层是双向链表:每个节点包含前后指针
  • 头尾增删快:时间复杂度 O(1)
  • 支持栈/队列操作:实现了 Deque 接口
  • 非线程安全
代码语言:javascript
复制
LinkedList<String> list = new LinkedList<>();
list.addFirst("Apple"); // 头插
list.addLast("Banana"); // 尾插
String first = list.getFirst(); // 快速获取第一个元素
🖼️ 图示:双向链表的内存布局
代码语言:javascript
复制
地址 2000: +--------+--------+--------+
         |prev=null| data=A |next=3000|
         +--------+--------+--------+

地址 3000: +--------+--------+--------+
         |prev=2000| data=B |next=null|
         +--------+--------+--------+

逻辑结构

代码语言:javascript
复制
head                                   tail
  |                                      |
  v                                      v
+--------+    +--------+
|null|A|<--->|A|B|null|
+--------+    +--------+
  2000          3000

适用场景:频繁在头部或尾部增删元素,如消息队列、栈操作。

❌ 经典误区:随机访问慢
代码语言:javascript
复制
// 错误:频繁随机访问,导致遍历整个链表,性能 O(n)
String third = list.get(2); // 需要遍历两次才能找到第三个元素

结论LinkedList 适合头尾操作多、随机访问少的场景。


三、List 的常用方法详解

1. 添加元素
代码语言:javascript
复制
// 在末尾添加
list.add("Apple");

// 在指定位置插入
list.add(1, "Banana"); // 插入到索引 1
2. 获取元素
代码语言:javascript
复制
// 通过索引获取
String first = list.get(0);

// 获取第一个/最后一个元素
String first = list.get(0);
String last = list.get(list.size() - 1);
3. 删除元素
代码语言:javascript
复制
// 删除指定位置的元素
list.remove(1); // 删除索引 1 的元素

// 删除指定对象(第一次出现)
list.remove("Apple");
4. 替换元素
代码语言:javascript
复制
// 替换指定位置的元素
list.set(1, "Grape"); // 将索引 1 的元素替换为 Grape
5. 查找元素
代码语言:javascript
复制
// 判断是否包含某个元素
boolean contains = list.contains("Apple");

// 查找元素的位置
int index = list.indexOf("Apple"); // 返回第一个 Apple 的索引
int lastIndex = list.lastIndexOf("Apple"); // 返回最后一个 Apple 的索引
6. 子列表操作
代码语言:javascript
复制
// 获取子列表
List<String> subList = list.subList(1, 3); // 包含索引 1 和 2 的元素

四、List 的线程安全版本

1. Collections.synchronizedList() —— 简单粗暴的同步
代码语言:javascript
复制
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

synchronized (syncList) {
    syncList.add("Apple");
}

缺点

  • 整个 List 对象加锁,粒度过大,性能差。
  • 不支持并发读写,可能导致阻塞。
2. CopyOnWriteArrayList —— 写时复制的线程安全 List
🎯 核心特性
  • 写时复制:每次修改(addremove)都会创建一个新数组副本。
  • 适用于读多写少的场景:读操作无锁,写操作加锁但不影响读。
代码语言:javascript
复制
List<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("Apple");
cowList.add("Banana");

适用场景

  • 并发读多、写少的场景,如日志记录、事件监听器等。

五、高频问题 & 高分回答

Q1: ArrayList 和 LinkedList 如何选择?

  • ArrayList:基于数组,查询快O(1)),增删慢O(n)); 适合随机访问多、增删少的场景。
  • LinkedList:基于链表,增删快O(1) 头尾),查询慢O(n)); 适合频繁头尾增删的场景,或实现栈/队列。 我们项目中用户列表用 ArrayList,消息队列用 LinkedList

Q2: ArrayList 的扩容机制是怎样的?

  • 默认容量 10;
  • 扩容时,新容量 = 原容量 × 1.5;
  • 使用 Arrays.copyOf() 复制数据;
  • 扩容是耗时操作,建议初始化时指定合理容量

Q3: LinkedList 的优势和劣势是什么?

  • 优势:头尾增删快(O(1)),支持栈/队列操作;
  • 劣势:随机访问慢(O(n)),内存开销稍大(每个节点有前后指针)。 适合频繁头尾操作的场景,如消息队列、栈操作。

Q4: CopyOnWriteArrayList 的工作原理?

  • 写时复制:每次修改(addremove)都会创建一个新数组副本;
  • 读操作无锁,写操作加锁但不影响读;
  • 适用于读多写少的场景,如日志记录、事件监听器等。

六、总结:一张表搞懂 List 的选型

场景

推荐实现

关键点

随机访问多、增删少

ArrayList

查询快,扩容注意

头尾增删多、随机访问少

LinkedList

头尾操作极快,随机访问慢

并发读多写少

CopyOnWriteArrayList

写时复制,读无锁


🔚 最后一句话

List 是 Java 集合框架中最基础、最常用的接口之一。 它不仅仅是一个“容器”,更是我们日常开发中处理有序数据的核心工具。 只有当你真正理解了 ArrayList 的扩容机制、LinkedList 的双向链表结构, 以及 CopyOnWriteArrayList 的写时复制原理, 你才能写出高效、健壮、专业的 Java 代码!

希望这篇能帮你彻底搞懂 List 接口及其常见实现!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、List 是什么?—— 有序可重复的“列表”
    • 🎯 核心特点
    • 🖼️ 图示:List 的逻辑结构
  • 二、List 的核心实现类
    • 1. ArrayList —— 基于“动态数组”的实现
      • 🎯 核心特性
      • 🔍 动态扩容机制
      • ❌ 经典误区:中间插入/删除慢
    • 2. LinkedList —— 基于“双向链表”的实现
      • 🎯 核心特性
      • 🖼️ 图示:双向链表的内存布局
      • ❌ 经典误区:随机访问慢
  • 三、List 的常用方法详解
    • 1. 添加元素
    • 2. 获取元素
    • 3. 删除元素
    • 4. 替换元素
    • 5. 查找元素
    • 6. 子列表操作
  • 四、List 的线程安全版本
    • 1. Collections.synchronizedList() —— 简单粗暴的同步
    • 2. CopyOnWriteArrayList —— 写时复制的线程安全 List
      • 🎯 核心特性
  • 五、高频问题 & 高分回答
    • Q1: ArrayList 和 LinkedList 如何选择?
    • Q2: ArrayList 的扩容机制是怎样的?
    • Q3: LinkedList 的优势和劣势是什么?
    • Q4: CopyOnWriteArrayList 的工作原理?
  • 六、总结:一张表搞懂 List 的选型
  • 🔚 最后一句话
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档