java基础-LinkedList(链表)

一、基本概念

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的场景:

  1. 频繁的插入和删除(尤其是头部操作)

  2. 需要实现队列、双端队列或栈

  3. 内存充足,不频繁随机访问

适合使用ArrayList的场景:

  1. 频繁随机访问

  2. 内存较紧张

  3. 主要在尾部添加元素

七、线程安全版本

java 复制代码
// 创建线程安全的LinkedList
List<String> syncList = Collections.synchronizedList(new LinkedList<>());

// 或者在多线程环境下使用ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentLinkedDeque;
ConcurrentLinkedDeque<String> concurrentDeque = new ConcurrentLinkedDeque<>();

八、注意事项

  1. 非线程安全:多线程环境下需要外部同步

  2. 迭代器快速失败 :迭代过程中修改会抛出ConcurrentModificationException

  3. 空元素:允许添加null元素

  4. 内存开销:每个元素都有额外的节点对象开销

九、示例: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取决于具体的应用场景和操作模式。

相关推荐
CoderYanger37 分钟前
A.每日一题——3512. 使数组和能被 K 整除的最少操作次数
java·数据结构·算法·leetcode·职场和发展·1024程序员节
雨中飘荡的记忆40 分钟前
设计模式之享元模式详解
java·设计模式·享元模式
梓德原1 小时前
【C语言】C语言如何向系统接要存
java·c语言·算法
WX-bisheyuange1 小时前
基于Spring Boot的流浪动物管理系统
java·spring boot·后端
阿蔹1 小时前
Selenium---控制窗口、manage()方法
java·selenium·测试工具·面试
兩尛1 小时前
HJ52 计算字符串的编辑距离
java·开发语言·算法
Mr-Wanter1 小时前
底层架构设计浅解
java·分布式·微服务
ArabySide1 小时前
【Spring Boot】Interceptor的原理、配置、顺序控制及与Filter的关键区别
java·spring boot·后端
武子康1 小时前
Java-183 OSS 上传实战:Java 原生与 Spring Boot 集成
java·开发语言·spring boot·分布式·spring·阿里云·oss