当前位置: 首页 > news >正文

Java集合框架:LinkedList

目录

文档链接

一、LinkedList 核心原理

1.1 底层数据结构

1.2 接口实现

1.3 重要特性

二、构造方法详解

2.1 无参构造方法

2.2 集合参数构造方法

三、核心方法详解与示例

3.1 元素添加方法

3.1.1 基本添加操作

3.1.2 首尾添加操作(Deque 特性)

3.1.3 栈操作

3.2 元素删除方法

3.2.1 基本删除操作

3.2.2 首尾删除操作(Deque 特性)

3.2.3 栈操作

3.3 元素查询方法

3.3.1 基本查询操作

3.3.2 队列风格查询(Deque 特性)

3.4 元素修改方法

3.5 其他常用方法

四、LinkedList 与 ArrayList 对比分析

五、线程安全问题

六、迭代器与快速失败机制

七、常见问题与注意事项

7.1 关于 null 元素

7.2 序列化问题

7.3 克隆问题


在 Java 集合框架中,LinkedList 是一个非常重要且常用的实现类。它以双向链表为底层数据结构,同时实现了 List 和 Deque 接口,兼具列表和双端队列的特性。

文档链接

点击这里,LinkedList

一、LinkedList 核心原理

1.1 底层数据结构

LinkedList 的底层实现是双向链表,每个节点(Node)包含三个部分:

  • 存储元素的值(item)
  • 指向前驱节点的引用(prev)
  • 指向后继节点的引用(next)

这种结构使得 LinkedList 在链表头部和尾部的操作具有天然优势,而随机访问则需要遍历链表。 

 LinkedList 的核心是其内部的 Node 类,这是一个典型的双向链表节点:

private static class Node<E> {E item;          // 存储元素Node<E> next;    // 指向后继节点Node<E> prev;    // 指向前驱节点// 构造方法,创建节点时需要指定前驱、元素和后继Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}
  • Node是私有的静态内部类,只能在 LinkedList 内部访问
  • 每个节点包含三个部分:元素本身、前驱节点引用、后继节点引用
  • 这种结构允许从任意节点向两个方向遍历链表

 LinkedList 通过以下几个字段维护整个链表结构:

transient Node<E> first;  // 指向头节点
transient Node<E> last;   // 指向尾节点
transient int size = 0;   // 元素数量
protected transient int modCount = 0;  // 从AbstractList继承,用于快速失败机制
  • firstlast指针分别指向链表的头和尾,使得首尾操作可以在 O (1) 时间内完成
  • size记录元素数量,避免每次需要大小时遍历整个链表
  • modCount用于记录结构修改次数,迭代器通过检查此值实现快速失败

1.2 接口实现

LinkedList 实现了以下关键接口:

  • List<E>:提供列表的基本操作(增删改查)
  • Deque<E>:提供双端队列的操作(首尾元素操作)
  • Cloneable:支持克隆
  • Serializable:支持序列化

这意味着 LinkedList 既可以作为普通列表使用,也可以作为队列、栈等数据结构使用。

1.3 重要特性

  • 允许 null 元素:可以添加 null 值到集合中
  • 非线程安全:     多线程环境下需要外部同步
  • 快速失败机制: 迭代器在检测到并发修改时会抛出 ConcurrentModificationException
  • 无容量限制:     链表可以动态增长,理论上只受内存限制

二、构造方法详解

LinkedList 提供了两个构造方法:

2.1 无参构造方法

创建一个空的 LinkedList:

// 构造空链表
LinkedList<String> list = new LinkedList<>();
System.out.println(list); // 输出:[]

2.2 集合参数构造方法

创建包含指定集合元素的 LinkedList,元素顺序与集合迭代器顺序一致:

// 从其他集合初始化
List<String> initial = Arrays.asList("a", "b", "c");
LinkedList<String> list = new LinkedList<>(initial);
System.out.println(list); // 输出:[a, b, c]

三、核心方法详解与示例

3.1 元素添加方法

3.1.1 基本添加操作

  • add(E e):                                                                 在链表尾部添加元素
  • add(int index, E element):                                  在指定位置插入元素
  • addAll(Collection<? extends E> c):                    在尾部添加集合所有元素
  • addAll(int index, Collection<? extends E> c):在指定位置插入集合所有元素
LinkedList<String> list = new LinkedList<>();// 在尾部添加元素
list.add("A");
list.add("B");
System.out.println(list); // [A, B]// 在指定位置插入
list.add(1, "C");
System.out.println(list); // [A, C, B]// 添加整个集合
List<String> newElements = Arrays.asList("D", "E");
list.addAll(newElements);
System.out.println(list); // [A, C, B, D, E]// 在指定位置添加集合
list.addAll(2, Arrays.asList("F", "G"));
System.out.println(list); // [A, C, F, G, B, D, E]

3.1.2 首尾添加操作(Deque 特性)

  • addFirst(E e):        在链表头部添加元素
  • addLast(E e):          在链表尾部添加元素(与 add 方法功能相同)
  • offer(E e):              在尾部添加元素(与 add 类似,队列风格)
  • offerFirst(E e):     在头部添加元素(与 addFirst 类似)
  • offerLast(E e):      在尾部添加元素(与 addLast 类似)
LinkedList<String> list = new LinkedList<>();list.addFirst("A");  // 头部添加
list.addLast("B");   // 尾部添加
System.out.println(list); // [A, B]list.offer("C");     // 尾部添加
list.offerFirst("D");// 头部添加
System.out.println(list); // [D, A, B, C]

3.1.3 栈操作

  • push(E e):将元素推入栈(实际上是在头部添加,与 addFirst 相同)
LinkedList<String> stack = new LinkedList<>();stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack); // [C, B, A]  栈顶是C

3.2 元素删除方法

3.2.1 基本删除操作

  • remove():                        删除并返回头部元素
  • remove(int index):        删除指定位置元素并返回
  • remove(Object o):          删除第一个匹配的元素,返回是否删除成功
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C", "B"));// 删除头部元素
String first = list.remove();
System.out.println(first); // A
System.out.println(list);  // [B, C, B]// 删除指定位置元素
String removed = list.remove(1);
System.out.println(removed); // C
System.out.println(list);    // [B, B]// 删除指定元素
boolean isRemoved = list.remove("B");
System.out.println(isRemoved); // true
System.out.println(list);      // [B]

3.2.2 首尾删除操作(Deque 特性)

  • removeFirst():                                删除并返回头部元素
  • removeLast():                                  删除并返回尾部元素
  • removeFirstOccurrence(Object o):删除第一次出现的元素
  • removeLastOccurrence(Object o):  删除最后一次出现的元素
  • poll():                        删除并返回头部元素(与 remove 类似,队列为空时返回 null)
  • pollFirst():                                    删除并返回头部元素(与 removeFirst 类似)
  • pollLast():                                      删除并返回尾部元素(与 removeLast 类似)
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C", "B", "D"));// 删除第一次出现的B
list.removeFirstOccurrence("B");
System.out.println(list); // [A, C, B, D]// 删除最后一次出现的B
list.removeLastOccurrence("B");
System.out.println(list); // [A, C, D]// poll系列方法
String head = list.poll();      // 移除头部
String last = list.pollLast();  // 移除尾部
System.out.println(head);  // A
System.out.println(last);  // D
System.out.println(list);  // [C]

3.2.3 栈操作

  • pop():弹出栈顶元素(实际上是删除头部元素,与 removeFirst 相同)
LinkedList<String> stack = new LinkedList<>(Arrays.asList("A", "B", "C"));String top = stack.pop();
System.out.println(top);   // A
System.out.println(stack); // [B, C]

3.3 元素查询方法

3.3.1 基本查询操作

  • get(int index):                返回指定位置的元素
  • getFirst():                       返回头部元素
  • getLast():                         返回尾部元素
  • contains(Object o):         判断是否包含指定元素
  • indexOf(Object o):          返回第一个匹配元素的索引
  • lastIndexOf(Object o):   返回最后一个匹配元素的索引
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C", "B"));// 获取指定位置元素
String element = list.get(2);
System.out.println(element); // C// 获取首尾元素
System.out.println(list.getFirst()); // A
System.out.println(list.getLast());  // B// 查找元素位置
System.out.println(list.indexOf("B"));    // 1
System.out.println(list.lastIndexOf("B"));// 3// 判断是否包含
System.out.println(list.contains("C"));   // true
System.out.println(list.contains("D"));   // false

3.3.2 队列风格查询(Deque 特性)

  • element():                返回头部元素(队列为空时抛异常)
  • peek():                      返回头部元素(队列为空时返回 null)
  • peekFirst():            返回头部元素(队列为空时返回 null)
  • peekLast():              返回尾部元素(队列为空时返回 null)
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));// 队列风格查询
System.out.println(list.element());    // A
System.out.println(list.peek());       // A
System.out.println(list.peekFirst());  // A
System.out.println(list.peekLast());   // C// 空列表测试
LinkedList<String> emptyList = new LinkedList<>();
System.out.println(emptyList.peek());  // null
// System.out.println(emptyList.element()); // 抛出NoSuchElementException

3.4 元素修改方法

  • set(int index, E element):替换指定位置的元素并返回旧元素
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));// 替换元素
String old = list.set(1, "X");
System.out.println(old);   // B
System.out.println(list);  // [A, X, C]

3.5 其他常用方法

  • size():                                  返回元素数量
  • clear():                                清空所有元素
  • clone():                                返回浅拷贝
  • toArray():                            转换为数组
  • toArray(T[] a):                   转换为指定类型数组
  • descendingIterator():        返回逆序迭代器
  • listIterator(int index):   返回从指定位置开始的列表迭代器
LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));// 基本信息
System.out.println(list.size()); // 3// 克隆
LinkedList<String> cloned = (LinkedList<String>) list.clone();
System.out.println(cloned); // [A, B, C]// 转换为数组
Object[] array = list.toArray();
String[] strArray = list.toArray(new String[0]);// 逆序迭代
Iterator<String> descIt = list.descendingIterator();
while (descIt.hasNext()) {System.out.print(descIt.next() + " "); // C B A
}// 列表迭代器
ListIterator<String> lit = list.listIterator(1);
while (lit.hasNext()) {System.out.print(lit.next() + " "); // B C
}// 清空列表
list.clear();
System.out.println(list.size()); // 0

四、LinkedList 与 ArrayList 对比分析

特性LinkedListArrayList
底层结构双向链表动态数组
随机访问慢(O (n))快(O (1))
头部插入 / 删除快(O (1))慢(O (n))
尾部插入 / 删除快(O (1))快(O (1))
中间插入 / 删除慢(O (n))慢(O (n))
内存占用较高(每个节点需要额外存储前后引用)较低(连续存储)
扩容机制无需扩容需要扩容(通常扩容为 1.5 倍)

选择建议

 
  • 需要频繁随机访问时,选择 ArrayList
  • 需要频繁在首尾操作元素时,选择 LinkedList
  • 需要频繁在中间插入删除时,视具体情况而定( LinkedList 需要先遍历到指定位置,也是 O (n) 操作)

五、线程安全问题

LinkedList 非线程安全的集合类。在多线程环境下,若有一个线程修改了集合结构(添加或删除元素),其他线程在迭代时可能会抛出 ConcurrentModificationException

线程安全解决方案

1、使用 Collections.synchronizedList() 包装:

List<String> syncList = Collections.synchronizedList(new LinkedList<>());

2、使用 java.util.concurrent 包下的 ConcurrentLinkedDeque(推荐):

Deque<String> concurrentDeque = new ConcurrentLinkedDeque<>();

 

六、迭代器与快速失败机制

LinkedList 的迭代器(包括 iterator 和 listIterator)采用快速失败(fail-fast)机制:当迭代器创建后,如果集合结构被修改(除了通过迭代器自身的方法),迭代器会立即抛出 ConcurrentModificationException

LinkedList<String> list = new LinkedList<>(Arrays.asList("A", "B", "C"));Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();if (s.equals("B")) {// 使用迭代器的remove方法是安全的it.remove(); // 直接使用list的remove方法会抛出异常// list.remove("B"); // 抛出ConcurrentModificationException}
}
System.out.println(list); // [A, C]

注意:

快速失败机制只是尽力而为,不能保证一定能检测到并发修改,因此不能依赖它来处理并发问题,只能用于调试。

 迭代方对比:

LinkedList<String> list = new LinkedList<>();
// 添加元素...// 方式1:for循环通过索引访问 - 性能最差
for (int i = 0; i < list.size(); i++) {String s = list.get(i);  // 每次get都要从头或尾遍历
}// 方式2:增强for循环 - 内部使用迭代器
for (String s : list) {// 操作元素
}// 方式3:显式使用迭代器 - 可以控制遍历过程
Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();// 可以调用it.remove()
}// 方式4:使用forEach方法(Java 8+)
list.forEach(s -> {// 操作元素
});
  • 避免使用 for 循环通过索引访问 LinkedList
  • 优先使用迭代器或增强 for 循环
  • 需要删除元素时,使用迭代器的 remove () 方法

七、常见问题与注意事项

7.1 关于 null 元素

LinkedList 允许添加 null 元素,并且可以正确处理:

LinkedList<String> list = new LinkedList<>();
list.add(null);
list.add("A");
list.add(null);System.out.println(list.contains(null));  // true
System.out.println(list.indexOf(null));   // 0
System.out.println(list.lastIndexOf(null)); // 2

 但需注意:在使用indexOf()lastIndexOf()时,null 值会被正确识别和比较。

7.2 序列化问题

LinkedList 实现了 Serializable 接口,但它的firstlastsize字段都被声明为transient,这意味着默认序列化机制不会序列化这些字段。

LinkedList 通过重写writeObject()readObject()方法实现了自定义序列化,只序列化元素本身,而不是整个节点结构,这样可以减小序列化后的大小。

7.3 克隆问题

LinkedList 的clone()方法返回的是浅拷贝:

LinkedList<List<String>> list = new LinkedList<>();
list.add(new ArrayList<>());LinkedList<List<String>> cloned = (LinkedList<List<String>>) list.clone();
// 克隆后的列表包含相同的元素引用
System.out.println(list.get(0) == cloned.get(0));  // true

这意味着:

  • 基本类型元素会被复制
  • 对象元素只会复制引用,不会创建新对象
  • 修改克隆列表中的对象元素会影响原列表
http://www.dtcms.com/a/309964.html

相关文章:

  • 【Jetson orin-nx】使用Tensorrt并发推理四个Yolo模型 (python版)
  • tensorflow目标分类:分绍(二)
  • 树莓派硬件介绍
  • 提示+掩膜+注意力=Mamba三连击,跨模态任务全面超越
  • 安检机危险品识别准确率↑23.7%:陌讯多模态融合算法实战解析
  • Python爬虫库性能与选型实战指南:从需求到落地的全链路解析
  • 神经网络的基础
  • 工业级蓝光三维扫描仪:汽车零部件高精度检测的利器
  • 研华PCI-1622C 使用RS-422通讯1主多从通讯中断
  • 【QT开发手册】对象模型(对象树) 窗⼝坐标体系
  • EXE加密软件(EXE一机一码加密大师) 最新版1.6.0更新 (附2025最新版本CSDN下载地址)
  • windows mamba-ssm环境配置指南
  • 网络层协议IP
  • 运维端口管理闭环:从暴露面测绘到自动化封禁!
  • 【AI问答记录】grafana接收query请求中未携带step参数,后端基于intervalMs和maxDataPoints等参数计算step的逻辑
  • AcWing 897:最长公共子序列 ← 子序列问题(n≤1e3)
  • “数据管理” 一场高风险的游戏
  • 民航领域数据分类分级怎么做?|《民航领域数据分类分级要求》标准解读
  • 第13届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2022年3月13日真题
  • ip去重小脚本
  • uniapp基础 (一)
  • git pull和git fetch的区别
  • Python爬虫实战:研究OpenCV技术构建图像数据处理系统
  • (转)mybatis和hibernate的 缓存区别?
  • (一)React +Ts(vite创建项目)
  • Flask 路由系统:URL 到 Python 函数的映射
  • 嵌入式学习笔记-MCU阶段-DAY10ESP8266模块
  • 第11届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2020年5月30日真题
  • 嵌入式软件 (SW) 设计文件
  • W3D引擎游戏开发----从入门到精通【10】