Java LinkedList

在 Java 中,LinkedList是集合框架中List接口的重要实现类,同时还实现了Deque(双端队列)和Queue接口,底层基于双向链表结构实现。它的特性与ArrayList(基于动态数组)形成鲜明对比,适用于频繁插入、删除的场景。本文将从底层结构、核心方法、特性对比、适用场景等方面详细解析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;
    }
}
  • 头节点:链表的第一个节点,prevnull
  • 尾节点:链表的最后一个节点,nextnull
  • 双向性:每个节点可通过prevnext访问前序和后续节点,便于双向遍历。

这种结构决定了LinkedList的核心特性:

  • 插入 / 删除效率高:只需修改节点的prevnext指针(已知位置时复杂度为O(1));
  • 查询效率低:访问指定索引的元素时,需从表头或表尾开始遍历(复杂度为O(n));
  • 内存开销大:每个节点除存储数据外,还需额外存储两个指针。

二、核心 API 与常用方法

LinkedList提供了丰富的方法,涵盖List接口的基础操作,以及Deque接口的队列 / 栈操作,以下是常用方法分类解析:

1. 构造方法

  • LinkedList():创建一个空的LinkedList
  • LinkedList(Collection<? extends E> c):通过已有集合初始化LinkedList(将集合元素按顺序添加到链表)。
 
 
// 空链表
LinkedList<String> list = new LinkedList<>();

// 用集合初始化
List<String> initList = Arrays.asList("a", "b", "c");
LinkedList<String> list2 = new LinkedList<>(initList); // 包含 ["a", "b", "c"]
 

2. 基础增删改查(List 接口)

方法功能描述时间复杂度
boolean add(E e) 在链表尾部添加元素 O(1)
void add(int index, E e) 在指定索引位置插入元素 O(n)(需先遍历到索引位置)
void addFirst(E e) 在链表头部添加元素(等价于push(e) O(1)
void addLast(E e) 在链表尾部添加元素(等价于add(e) O(1)
E remove() 删除并返回链表头部元素(等价于removeFirst() O(1)
E remove(int index) 删除并返回指定索引的元素 O(n)
boolean remove(Object o) 删除第一个匹配的元素(需 equals 判断) O(n)
E removeFirst() 删除并返回头部元素 O(1)
E removeLast() 删除并返回尾部元素 O(1)
E get(int index) 获取指定索引的元素 O(n)
E getFirst() 获取头部元素 O(1)
E getLast() 获取尾部元素 O(1)
E set(int index, E e) 替换指定索引的元素并返回旧值 O(n)
int size() 返回元素个数 O(1)
boolean isEmpty() 判断链表是否为空 O(1)

3. 队列 / 栈操作(Deque 接口)

由于LinkedList实现了Deque,可直接作为队列(FIFO) 或栈(LIFO) 使用:

场景方法(功能)说明
队列 boolean offer(E e):尾部添加元素 成功返回true(队列满时返回false,但LinkedList无容量限制,等价于add(e)
  E poll():删除并返回头部元素 队列为空时返回null(区别于remove()的抛异常)
  E peek():获取头部元素(不删除) 队列为空时返回null(区别于getFirst()的抛异常)
void push(E e):头部添加元素 等价于addFirst(e)
  E pop():删除并返回头部元素 等价于removeFirst()(栈为空时抛NoSuchElementException

4. 遍历方法

LinkedList支持多种遍历方式,效率因方式而异:

  • 迭代器遍历(推荐,支持边遍历边删除):
     
    LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c"));
    
    // 正向遍历
    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
        System.out.println(it.next()); // a, b, c
    }
    
    // 双向遍历(ListIterator支持向前/向后移动)
    ListIterator<String> lit = list.listIterator(list.size()); // 从尾部开始
    while (lit.hasPrevious()) {
        System.out.println(lit.previous()); // c, b, a
    }
    
     
  • 增强 for 循环(底层还是迭代器):
     
    for (String s : list) {
        System.out.println(s);
    }
    
     
  • 索引遍历(不推荐,效率低,每次get(i)都需从头遍历):
     
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i)); // 时间复杂度O(n²)
    }
    
     

三、LinkedList vs ArrayList:核心区别

特性LinkedListArrayList
底层结构 双向链表 动态数组(容量不足时扩容为 1.5 倍)
内存开销 高(每个节点需存储前后指针) 低(仅存储数据,可能有冗余容量)
随机访问(get(i) 慢(O(n)),需遍历 快(O(1)),直接通过索引访问
插入 / 删除(中间) 快(O(1),已知前后节点时) 慢(O(n),需移动后续元素)
插入 / 删除(头尾) 快(O(1) 尾部快(O(1)),头部慢(O(n)
线程安全性 不安全(多线程操作需同步) 不安全(同左)
适用场景 频繁插入 / 删除、实现队列 / 栈 频繁查询、随机访问

四、注意事项

  1. 线程不安全LinkedList未实现同步,多线程环境下同时修改会导致数据不一致。解决方式:
    • Collections.synchronizedList()包装:
       
      List<String> syncList = Collections.synchronizedList(new LinkedList<>());
      
       
    • 改用线程安全的ConcurrentLinkedDeque(JUC 包)。
  2. 遍历中删除元素
    • 用迭代器的remove()方法(安全):
       
      Iterator<String> it = list.iterator();
      while (it.hasNext()) {
          if (it.next().equals("a")) {
              it.remove(); // 正确:迭代器内部维护指针,避免ConcurrentModificationException
          }
      }
      
       
    • 不可用for循环直接remove(i)(会导致索引偏移,漏删或越界)。
  3. 避免频繁get(i):因get(i)需从头 / 尾遍历,频繁调用会导致效率极低(如遍历链表用get(i)时间复杂度为O(n²))。

五、适用场景

  • 频繁插入 / 删除:尤其是在链表中间位置(如实现链表式数据结构、日志记录动态增删);
  • 队列 / 栈实现:利用Deque接口的方法,简洁实现 FIFO 队列或 LIFO 栈;
  • 双向遍历需求:需向前 / 向后遍历元素(如浏览器历史记录的前进 / 后退)。

总结

LinkedList是基于双向链表的集合类,以牺牲查询效率为代价,换取了高效的插入 / 删除操作和灵活的双向操作能力。在选择LinkedList还是ArrayList时,核心依据是操作类型:

  • 若以查询、随机访问为主,选ArrayList
  • 若以插入、删除(尤其是中间位置) 或队列 / 栈操作为主,选LinkedList

掌握其底层结构和方法特性,能帮助开发者在实际场景中做出最优选择,提升程序性能。

posted on 2025-09-11 09:05  coding博客  阅读(20)  评论(0)    收藏  举报