一、基本概念
LinkedList 是Java集合框架中实现List接口和Deque接口的双向链表。
java
// 类定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
二、核心特性
1. 双向链表结构
java
private static class Node<E> {
E item; // 存储的数据
Node<E> next; // 下一个节点
Node<E> prev; // 上一个节点
}
2. 主要特点
-
动态大小:无需指定初始容量
-
非连续内存:通过指针连接
-
插入/删除高效:O(1)时间复杂度(已知位置时)
-
随机访问较慢:O(n)时间复杂度
-
线程不安全:需要外部同步
三、常用操作
1. 创建LinkedList
java
// 创建空链表
LinkedList<String> list1 = new LinkedList<>();
// 从其他集合创建
List<Integer> numbers = Arrays.asList(1, 2, 3);
LinkedList<Integer> list2 = new LinkedList<>(numbers);
2. 基本操作示例
java
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.addFirst("First"); // 添加到头部
list.addLast("Last"); // 添加到尾部
list.add(2, "Orange"); // 插入到指定位置
System.out.println("初始列表: " + list);
// 输出: [First, Apple, Orange, Banana, Last]
// 访问元素
System.out.println("第一个元素: " + list.getFirst());
System.out.println("最后一个元素: " + list.getLast());
System.out.println("索引2的元素: " + list.get(2));
// 删除元素
list.removeFirst(); // 删除头部
list.removeLast(); // 删除尾部
list.remove(1); // 删除指定位置
list.remove("Orange"); // 删除指定元素
// 修改元素
list.set(0, "Grape");
// 大小和判断
System.out.println("大小: " + list.size());
System.out.println("是否包含Grape: " + list.contains("Grape"));
System.out.println("是否为空: " + list.isEmpty());
}
}
3. 作为队列使用(Queue)
java
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.offer("Task1"); // 尾部添加
queue.offer("Task2");
queue.offer("Task3");
// 查看队首
System.out.println("队首: " + queue.peek()); // Task1
// 出队
while (!queue.isEmpty()) {
System.out.println("处理: " + queue.poll()); // 移除并返回队首
}
4. 作为双端队列使用(Deque)
java
LinkedList<Integer> deque = new LinkedList<>();
// 从两端添加
deque.addFirst(1); // 头部添加
deque.addLast(2); // 尾部添加
deque.offerFirst(0); // 头部添加
deque.offerLast(3); // 尾部添加
// 从两端获取和移除
System.out.println("头部: " + deque.peekFirst()); // 查看头部
System.out.println("尾部: " + deque.peekLast()); // 查看尾部
System.out.println("移除头部: " + deque.pollFirst()); // 移除头部
System.out.println("移除尾部: " + deque.pollLast()); // 移除尾部
5. 作为栈使用(Stack)
java
LinkedList<String> stack = new LinkedList<>();
// 压栈
stack.push("Data1");
stack.push("Data2");
stack.push("Data3");
// 弹栈
while (!stack.isEmpty()) {
System.out.println("弹出: " + stack.pop()); // 后进先出
}
四、遍历方式
java
LinkedList<String> list = new LinkedList<>();
list.add("A");
list.add("B");
list.add("C");
// 1. for循环(通过索引)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i)); // 效率较低
}
// 2. 增强for循环
for (String item : list) {
System.out.println(item);
}
// 3. 迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 4. 双向迭代器
ListIterator<String> listIterator = list.listIterator();
// 向前遍历
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
// 向后遍历
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
// 5. forEach方法(Java 8+)
list.forEach(item -> System.out.println(item));
list.forEach(System.out::println);
五、性能对比
| 操作 | ArrayList | LinkedList |
|---|---|---|
| 随机访问 | O(1) | O(n) |
| 头部插入/删除 | O(n) | O(1) |
| 尾部插入/删除 | O(1) | O(1) |
| 中间插入/删除 | O(n) | O(n)* |
| 内存占用 | 较低(连续) | 较高(节点开销) |
注:LinkedList在中间插入也需要先找到位置,所以也是O(n)
六、使用场景建议
适合使用LinkedList的场景:
-
频繁的插入和删除(尤其是头部操作)
-
需要实现队列、双端队列或栈
-
内存充足,不频繁随机访问
适合使用ArrayList的场景:
-
频繁随机访问
-
内存较紧张
-
主要在尾部添加元素
七、线程安全版本
java
// 创建线程安全的LinkedList
List<String> syncList = Collections.synchronizedList(new LinkedList<>());
// 或者在多线程环境下使用ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentLinkedDeque;
ConcurrentLinkedDeque<String> concurrentDeque = new ConcurrentLinkedDeque<>();
八、注意事项
-
非线程安全:多线程环境下需要外部同步
-
迭代器快速失败 :迭代过程中修改会抛出
ConcurrentModificationException -
空元素:允许添加null元素
-
内存开销:每个元素都有额外的节点对象开销
九、示例:LRU缓存实现
java
class LRUCache<K, V> {
private final int capacity;
private final LinkedList<K> keys;
private final Map<K, V> cache;
public LRUCache(int capacity) {
this.capacity = capacity;
this.keys = new LinkedList<>();
this.cache = new HashMap<>();
}
public V get(K key) {
if (cache.containsKey(key)) {
keys.remove(key); // 移除旧位置
keys.addFirst(key); // 放到头部(最近使用)
return cache.get(key);
}
return null;
}
public void put(K key, V value) {
if (cache.containsKey(key)) {
keys.remove(key);
} else if (keys.size() >= capacity) {
K oldest = keys.removeLast(); // 移除最久未使用
cache.remove(oldest);
}
keys.addFirst(key);
cache.put(key, value);
}
}
总结
LinkedList是Java中重要的数据结构,特别适合需要频繁插入删除的场景。它不仅是List,还可以作为Queue、Deque和Stack使用。选择LinkedList还是ArrayList取决于具体的应用场景和操作模式。