Java:LinkedList的使用
目录
一、概念
二、常用操作
2.1 基于List的操作
2.2 队列(Queue)、栈(Stack)和双端队列(Deque)操作
2.3 遍历
一、概念
LinkedList
在 Java 中是一个基于双向链表实现的 List
接口的实现类。它同时实现了 List
和 Deque
(双端队列)接口,所以它同时具备了列表和队列的特性。
核心特点:
-
非连续存储:元素分散存储在堆内存中,通过引用相连。这使其在内存利用上更灵活,但每个元素需要额外的空间来存储前后节点的引用(内存开销更大)。
-
动态大小:与数组不同,链表的大小是动态的,可以自由地添加和删除元素,无需担心容量问题。
-
插入和删除高效:在已知位置(通过迭代器定位)进行插入和删除操作的时间复杂度是 O(1)。这是它相对于
ArrayList
最大的优势。因为它只需要修改相邻节点的引用即可,而不需要像数组那样移动大量元素。 -
随机访问低效:根据索引访问元素(
get(i)
,set(i, element)
)的时间复杂度是 O(n)。因为它必须从链表的头部(或尾部,JDK 做了优化)开始遍历,直到找到第 i 个节点。 -
实现了双端队列(Deque):提供了丰富的方法,如
addFirst()
,addLast()
,removeFirst()
,removeLast()
,getFirst()
,getLast()
等,可以非常方便地将其用作栈(Stack)、队列(Queue)或双端队列(Deque)。
二、常用操作
2.1 基于List的操作
LinkedList<String> list = new LinkedList<>();// 添加元素
list.add("Apple"); // 添加到尾部
list.add(1, "Banana"); // 在指定索引位置插入
list.addFirst("First"); // 添加到头部 (Deque 方法)
list.addLast("Last"); // 添加到尾部 (Deque 方法,等同于 add())// 获取元素
String first = list.get(0); // 根据索引获取
String head = list.getFirst(); // 获取头部元素
String tail = list.getLast(); // 获取尾部元素// 删除元素
list.remove(1); // 根据索引删除
list.remove("Apple"); // 根据元素内容删除(首次出现)
list.removeFirst(); // 删除并返回头部元素
list.removeLast(); // 删除并返回尾部元素// 检查大小和内容
int size = list.size();
boolean isEmpty = list.isEmpty();
boolean hasApple = list.contains("Apple");// 替换元素
list.set(0, "New First"); // 将索引0位置的元素替换
2.2 队列(Queue)、栈(Stack)和双端队列(Deque)操作
LinkedList<String> queue = new LinkedList<>();// 作为普通队列 (FIFO: First-In-First-Out)
queue.offer("A"); // 入队(添加到尾部),推荐使用
queue.add("B"); // 入队(添加到尾部),失败会抛异常
String s1 = queue.poll(); // 出队(移除并返回头部元素),队列空则返回null
String s2 = queue.remove(); // 出队,队列空则抛异常 NoSuchElementException
String s3 = queue.peek(); // 获取但不移除头部元素,空则返回null
String s4 = queue.element(); // 获取但不移除头部元素,空则抛异常// 作为栈 (LIFO: Last-In-First-Out)
LinkedList<String> stack = new LinkedList<>();
stack.push("A"); // 压栈(添加到头部)
stack.push("B");
String top = stack.pop(); // 出栈(移除并返回头部元素)
String top2 = stack.peek(); // 查看栈顶元素// 作为双端队列 (Deque)
queue.offerFirst("A"); // 添加到头部
queue.offerLast("Z"); // 添加到尾部(等同于 offer)
String first = queue.pollFirst(); // 从头部移除
String last = queue.pollLast(); // 从尾部移除
典型使用场景:
-
实现栈(LIFO):
addFirst()
/removeFirst()
-
实现队列(FIFO):
offer()
/poll()
或addLast()
/removeFirst()
-
实现双端队列:各种
First
/Last
方法
操作 | 方法示例 | 时间复杂度 | 说明 |
---|---|---|---|
头部插入 | addFirst(e) , offerFirst(e) | O(1) | |
尾部插入 | add(e) , addLast(e) , offer(e) , offerLast(e) | O(1) | |
指定位置插入 | add(index, e) | O(n) | 主要耗时在遍历到 index 位置 |
头部删除 | removeFirst() , pollFirst() | O(1) | |
尾部删除 | removeLast() , pollLast() | O(1) | |
指定元素/位置删除 | remove(o) , remove(index) | O(n) | 主要耗时在遍历找到元素或位置 |
随机访问(获取) | get(index) | O(n) | 需要遍历 |
随机访问(修改) | set(index, e) | O(n) | 需要遍历到位置后再修改,修改本身是 O(1) |
查找元素位置 | indexOf(o) | O(n) | 需要遍历比较 |
检查是否包含 | contains(o) | O(n) | 内部调用 indexOf |
队列使用示例:
import java.util.LinkedList;
import java.util.Queue;public class QueueExample {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();// 生产者:入队10个任务for (int i = 0; i < 10; i++) {queue.offer(i);System.out.println("任务入队: " + i);}// 消费者:处理所有任务while (!queue.isEmpty()) {Integer task = queue.poll();System.out.println("处理任务: " + task);}}
}
2.3 遍历
千万不要用 for-i
循环(基于索引的循环)来遍历 LinkedList
!
因为每次 get(i)
都会从链表头开始遍历,会导致时间复杂度变为 O(n²),性能极差。
// foreach 循环(推荐):语法简洁,底层使用迭代器,效率高。
for (String item : list) {System.out.println(item);
}// 迭代器(Iterator):更灵活,可以在遍历时使用 iterator.remove() 安全地删除元素。
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String item = iterator.next();if ("某些条件") {iterator.remove(); // 安全删除!}
}// ListIterator:功能更强大的迭代器,可以双向遍历(向前/向后)以及在遍历时添加和修改元素。
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {String next = listIterator.next();listIterator.set("Modified: " + next); // 修改元素listIterator.add("Added"); // 添加元素
}