Java 集合框架:LinkedList 的介绍、使用、原理与源码解析

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 014 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。当然,我也会在必要的时候进行相关技术深度的技术解读,相信即使是拥有多年 Java 开发经验的从业者和大佬们也会有所收获并找到乐趣。

Java 集合框架中包含了多种用于数据存储和操作的类,其中 LinkedList 是一个重要且常用的实现。LinkedList 作为一个双向链表,提供了高效的插入和删除操作,特别适用于频繁进行元素增删的场景。对于很多开发者而言,深入理解 LinkedList 的特性和工作原理是提高代码性能和优化数据处理逻辑的关键。本文将对 LinkedList 进行全面的介绍和解析,帮助读者掌握其使用方法、内部原理以及源码实现。


文章目录

      • 1、LinkedList 概述
      • 2、LinkedList 的具体实现原理
        • 2.1、LinkedList 底层的数据结构
        • 2.2、LinkedList 增删改查的实现
          • 2.2.1、LinkedList 的插入
          • 2.2.2、LinkedList 的删除
          • 2.2.3、LinkedList 的修改
          • 2.2.4、LinkedList 的查询
        • 2.3、LinkedList 对链表结构的实现
      • 3、LinkedList 相关知识点
        • 3.1、关于 Queue 队列
        • 3.2、关于 ArrayList 和 LinkedList 的区别
        • 3.3、算法:翻转链表
      • 4、LinkedList 的使用(常用方法)
        • 4.1、LinkedList 的常用方法
        • 4.2、继承自 `Queue` 接口的方法
        • 4.3、Collections 类中涉及 LinkedList 的常用方法


1、LinkedList 概述

LinkedList 是 List 接口的一个实现,它是一个双向链表。与 ArrayList 相比,LinkedList 的元素在内存中不是连续存放的。每个元素(称为节点)都包含两部分数据:一部分是存储的数据,另一部分是指向前一个节点和后一个节点的链接(指针)。

LinkedList 的优点在于插入和删除元素时效率很高。因为链表的数据结构使得它只需要修改前后节点的指针即可,而不需要像 ArrayList 那样移动其他元素。因此,LinkedList 适合用在需要频繁执行添加和删除操作的场景。

然而,LinkedList 的随机访问效率不高,每次查找某个元素都需要从头开始遍历链表直到找到目标元素。这使得 LinkedList 在执行随机访问操作时速度较慢,不如 ArrayList。

在 Java 中,LinkedList 除了实现 List 接口外,还实现了 Deque 接口,这意味着它还可以被当作队列(Queue)、双端队列(Deque)使用,提供了更加丰富的方法,如 addFirst(), addLast(), removeFirst(), removeLast() 等。

LinkedList 也是非线程安全的。如果在多线程环境中使用,需要外部同步或者考虑使用 java.util.concurrent 包中的线程安全类,如 LinkedBlockingDeque 等。

总的来说,LinkedList 由于其结构的特点,适合于数据量不大但插入和删除操作频繁的场景,而不适合大量的随机访问操作。


2、LinkedList 的具体实现原理

2.1、LinkedList 底层的数据结构

LinkedList 是基于链表数据结构实现的,它包含一系列的节点(Node),每个节点包括三个部分:前一个节点的引用(previous),储存的元素(data),和下一个节点的引用(next)。这种数据结构支持高效的元素插入和删除操作。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{transient int size = 0;// 链表中元素的数量transient int size = 0;/*** 指向第一个节点的指针*/transient Node<E> first;/*** 指向最后一个节点的指针*/transient Node<E> last;/*** 构造一个空的链表*/public LinkedList() {}/*** 构造一个包含指定集合元素的链表,这些元素按集合的迭代器返回的顺序排列。** @param  c 要放入链表中的集合* @throws NullPointerException 如果指定的集合为 null*/public LinkedList(Collection<? extends E> c) {this();// 将集合中的所有元素添加到链表中addAll(c);}// 省略其他方法和实现细节.../*** Node 是一个私有的静态内部类,用于表示 LinkedList 中的每个节点** @param <E>*/private static class Node<E> {// 节点存储的元素E item;// 指向下一个节点的引用Node<E> next;// 指向前一个节点的引用Node<E> prev;/*** 构造方法,创建一个新的节点** @param prev    该节点的前一个节点* @param element 该节点存储的元素* @param next    该节点的后一个节点*/Node(Node<E> prev, E element, Node<E> next) {// 设置节点存储的元素this.item = element;// 设置指向下一个节点的引用this.next = next;// 设置指向前一个节点的引用this.prev = prev;}}// 省略其他方法和实现细节...
}
2.2、LinkedList 增删改查的实现
2.2.1、LinkedList 的插入

LinkedList 的插入的实现并不复杂,其插入式,主要会有两种场景,一种是在链表的末尾插入,另一种是在指定节点之前插入一个新节点,二者的实现都是通过创建一个新节点并更新其前驱和后继节点的引用,唯一的区别就是,linkBefore 是在指定节点之后插入该新节点,而 linkAfter 是在指定节点之后插入该新节点。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 在链表末尾添加一个元素** @param e 要添加的元素* @return 总是返回 true*/@Overridepublic boolean add(E e) {// 在链表末尾链接一个新的节点linkLast(e);return true;}/*** 在链表末尾链接一个新的节点** @param e 新节点存储的元素*/void linkLast(E e) {// 保存当前链表的最后一个节点final Node<E> l = last;// 创建一个新的节点,前驱节点是当前最后一个节点,后继节点是 nullfinal Node<E> newNode = new Node<>(l, e, null);// 将链表的最后一个节点更新为新节点last = newNode;if (l == null)// 如果链表之前是空的,即没有任何元素// 将新节点设置为链表的第一个节点first = newNode;else// 如果链表中已有元素// 将原最后一个节点的 next 指针指向新节点l.next = newNode;// 链表大小增加 1size++;  }/*** 在指定索引处插入元素** @param index 要插入元素的位置* @param element 要插入的元素*/public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));}/*** 在指定节点之前插入一个新节点** @param e 新节点存储的元素* @param succ 指定的节点,新节点将插入在其之前*/void linkBefore(E e, Node<E> succ) {// assert succ != null;  // 确保 succ 不为 null,确保在调用该方法前 succ 一定存在// 保存指定节点的前驱节点final Node<E> pred = succ.prev;// 创建一个新的节点,其前驱是 pred,后继是 succfinal Node<E> newNode = new Node<>(pred, e, succ);// 将指定节点的前驱更新为新节点succ.prev = newNode;if (pred == null)// 如果 pred 为 null,说明 succ 是第一个节点// 将新节点设置为链表的第一个节点first = newNode;else// 如果 pred 不为 null,说明 succ 不是第一个节点// 将 pred 的后继节点更新为新节点pred.next = newNode;// 链表大小增加 1size++;// 修改计数器增加 1,用于快速失败机制modCount++;  }// 省略其他方法和实现细节...
}

此外,另一个十分值得注意的点是,add(int index, E element) 在调用指定节点之前插入一个新节点方法时,调用了 node(int index) 方法来返回指定索引处的节点。该方法可以看作是 LinkedList 的核心实现之一,除插入方法之外,LinkedList 在查询、删除、修改等方法除也需要调用当前方法:

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 返回指定索引处的节点。** @param index 要获取节点的位置* @return 指定索引处的节点*/Node<E> node(int index) {// assert isElementIndex(index);  // 确保索引在有效范围内// 判断索引是否在链表的前半部分if (index < (size >> 1)) {// 从链表的第一个节点开始遍历Node<E> x = first;// 遍历到指定索引位置for (int i = 0; i < index; i++)// 依次移动到下一个节点x = x.next;// 返回找到的节点return x;} else {// 索引在链表的后半部分Node<E> x = last;// 从链表的最后一个节点开始遍历for (int i = size - 1; i > index; i--)// 依次移动到前一个节点x = x.prev;// 返回找到的节点return x;}}// 省略其他方法和实现细节...
}
2.2.2、LinkedList 的删除

LinkedList 的删除的实现同样十分简单,是使用 unlink 方法,即通过更新前驱和后继节点的引用,移除指定节点并返回其存储的元素实现的。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 解除并移除指定节点。** @param x 要移除的节点* @return 被移除节点存储的元素*/E unlink(Node<E> x) {// assert x != null;  // 确保 x 不为 null// 保存节点中的元素final E element = x.item;// 保存节点的后继节点final Node<E> next = x.next;// 保存节点的前驱节点final Node<E> prev = x.prev;if (prev == null) {// 如果前驱节点为 null,说明 x 是第一个节点// 将链表的第一个节点更新为 x 的后继节点first = next;} else {// 如果前驱节点不为 null// 将前驱节点的后继更新为 x 的后继节点prev.next = next;// 将 x 的前驱设为 null,帮助垃圾回收x.prev = null;}if (next == null) {// 如果后继节点为 null,说明 x 是最后一个节点// 将链表的最后一个节点更新为 x 的前驱节点last = prev;} else {// 如果后继节点不为 null// 将后继节点的前驱更新为 x 的前驱节点next.prev = prev;// 将 x 的后继设为 null,帮助垃圾回收x.next = null;}// 将 x 中的元素设为 null,帮助垃圾回收x.item = null;// 链表大小减 1size--;// 修改计数器增加 1,用于快速失败机制modCount++;// 返回被移除节点中的元素return element;}/*** 移除指定索引处的元素。** @param index 要移除元素的位置* @return 被移除的元素*/public E remove(int index) {checkElementIndex(index);return unlink(node(index));}/*** 从链表中移除第一次出现的指定元素(如果存在)。* 如果列表不包含该元素,则不做改变。** @param o 要移除的元素* @return 如果列表包含指定元素并移除成功则返回 true,否则返回 false*/public boolean remove(Object o) {// 如果要移除的元素为 nullif (o == null) { for (Node<E> x = first; x != null; x = x.next) {// 从链表的第一个节点开始遍历if (x.item == null) {// 如果当前节点的元素为 nullunlink(x);  // 解除并移除当前节点// 返回 true,表示移除成功return true;  }}} else {  // 如果要移除的元素不为 nullfor (Node<E> x = first; x != null; x = x.next) {  // 从链表的第一个节点开始遍历// 如果当前节点的元素等于要移除的元素if (o.equals(x.item)) {// 解除并移除当前节点unlink(x);// 返回 true,表示移除成功return true;  }}}// 如果没有找到要移除的元素,返回 falsereturn false;  }// 省略其他方法和实现细节...
}
2.2.3、LinkedList 的修改

LinkedList 对于修改的则是通过找到原位置的 Node 并修改其 item 值实现的。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 替换指定索引处的元素,并返回被替换的元素。** @param index 要替换元素的位置* @param element 要设置的新元素* @return 被替换的旧元素* @throws IndexOutOfBoundsException 如果索引超出范围*/public E set(int index, E element) {// 检查索引是否在范围内checkElementIndex(index);// 获取指定索引处的节点Node<E> x = node(index);// 保存当前节点的旧元素E oldVal = x.item;// 将节点的元素设置为新元素x.item = element;// 返回被替换的旧元素return oldVal;}/*** 将迭代器返回的最后一个元素替换为指定的元素。** @param e 要设置的新元素* @throws IllegalStateException 如果没有调用 next() 或 previous() 方法*/public void set(E e) {if (lastReturned == null)// 如果 lastReturned 为 null,表示未调用 next() 或 previous()// 抛出非法状态异常throw new IllegalStateException();// 检查是否发生并发修改checkForComodification();// 将 lastReturned 节点的元素设置为新元素 elastReturned.item = e;}// 省略其他方法和实现细节...
}
2.2.4、LinkedList 的查询

LinkedList 的查询即获取指定索引处的元素。它的实现同前面的其他方法获取索引处元素一样,依赖 node(int index) 方法。

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 获取指定索引处的元素** @param index 要获取元素的位置* @return 指定索引处的元素*/public E get(int index) {checkElementIndex(index);return node(index).item;}/*** 返回指定索引处的节点。** @param index 要获取节点的位置* @return 指定索引处的节点*/Node<E> node(int index) {// assert isElementIndex(index);  // 确保索引在有效范围内// 判断索引是否在链表的前半部分if (index < (size >> 1)) {// 从链表的第一个节点开始遍历Node<E> x = first;// 遍历到指定索引位置for (int i = 0; i < index; i++)// 依次移动到下一个节点x = x.next;// 返回找到的节点return x;} else {// 索引在链表的后半部分Node<E> x = last;// 从链表的最后一个节点开始遍历for (int i = size - 1; i > index; i--)// 依次移动到前一个节点x = x.prev;// 返回找到的节点return x;}}// 省略其他方法和实现细节...
}
2.3、LinkedList 对链表结构的实现

LinkedList 类实现了 Queue 接口,因此提供了队列相关的方法。这些方法允许 LinkedList 作为一个双端队列(Deque)使用,支持在队列的头部和尾部进行插入和删除操作。下面是对 LinkedList 中与 Queue 相关方法的概括说明:

  • offer(E e):将指定的元素添加到队列的尾部并返回 true。实现:调用 linkLast(E e) 方法,将元素添加到链表的尾部;
  • poll():获取并移除此队列的头部,如果队列为空,则返回 null。实现:调用 unlinkFirst(Node<E> f) 方法,移除并返回链表的第一个元素;
  • remove():获取并移除此队列的头部,如果队列为空,则抛出 NoSuchElementException。实现:调用 unlinkFirst(Node<E> f) 方法,移除并返回链表的第一个元素。如果链表为空,抛出 NoSuchElementException
  • peek():获取但不移除此队列的头部;如果队列为空,则返回 null。实现:返回链表的第一个元素 first.item,如果链表为空返回 null
  • element():获取但不移除此队列的头部;如果队列为空,则抛出 NoSuchElementException。实现:返回链表的第一个元素 first.item,如果链表为空抛出 NoSuchElementException

具体实现:

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 将指定的元素添加到队列的尾部。** @param e 要添加的元素* @return 添加成功返回 true*/public boolean offer(E e) {// 调用 add 方法,将元素添加到链表的尾部return add(e);}/*** 获取并移除此队列的头部,如果队列为空,则返回 null。** @return 队列头部的元素,如果队列为空则返回 null*/public E poll() {// 保存链表的第一个节点final Node<E> f = first;// 如果链表为空返回 null,否则移除并返回第一个元素return (f == null) ? null : unlinkFirst(f);}/*** 获取并移除此队列的头部,如果队列为空,则抛出 NoSuchElementException。** @return 队列头部的元素* @throws NoSuchElementException 如果队列为空*/public E remove() {// 保存链表的第一个节点final Node<E> f = first;// 如果链表为空,抛出 NoSuchElementExceptionif (f == null)throw new NoSuchElementException();// 移除并返回第一个元素return unlinkFirst(f);}/*** 获取但不移除此队列的头部;如果队列为空,则返回 null。** @return 队列头部的元素,如果队列为空则返回 null*/public E peek() {// 保存链表的第一个节点final Node<E> f = first;// 返回第一个元素但不移除,如果链表为空返回 nullreturn (f == null) ? null : f.item;}/*** 获取但不移除此队列的头部;如果队列为空,则抛出 NoSuchElementException。** @return 队列头部的元素* @throws NoSuchElementException 如果队列为空*/public E element() {return getFirst();}/*** 返回链表的第一个元素;如果链表为空,则抛出 NoSuchElementException。** @return 链表的第一个元素* @throws NoSuchElementException 如果链表为空*/public E getFirst() {// 保存链表的第一个节点final Node<E> f = first;// 如果链表为空,抛出 NoSuchElementExceptionif (f == null)throw new NoSuchElementException();// 返回第一个元素return f.item;}// 省略其他方法和实现细节...
}

内部辅助方法,unlinkFirst(Node<E> f):移除并返回链表的第一个节点。实现:更新链表的头节点 first 为当前头节点 f 的下一个节点 f.next,并将旧头节点的前驱引用设为 null

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{// 省略其他方法和实现细节.../*** 移除并返回链表的第一个节点。** @param f 要移除的第一个节点* @return 被移除节点存储的元素*/private E unlinkFirst(Node<E> f) {// 保存第一个节点的元素final E element = f.item;// 保存第一个节点的后继节点final Node<E> next = f.next;// 清空第一个节点的元素f.item = null;// 清空第一个节点的后继引用,帮助垃圾回收f.next = null;// 更新链表的头节点为原头节点的后继节点first = next;// 如果新的头节点为 null,说明链表现在为空if (next == null) {// 更新链表的尾节点为 nulllast = null;} else {// 将新的头节点的前驱引用设为 nullnext.prev = null;}// 链表大小减 1size--;// 修改计数器增加 1,用于快速失败机制modCount++;// 返回被移除的元素return element;}// 省略其他方法和实现细节...
}

3、LinkedList 相关知识点

3.1、关于 Queue 队列

队列(Queue):也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。

image-20240611154341484

这和我们日常生活中的排队是一致的,最早排队的也是最早离队的。其操作的特性是先进先出(First In First Out, FIFO),故又称为先进先出的线性表。基本上,一个队列就是一个先入先出(FIFO)的数据结构

在Java 中 Queue 接口与 List、Set 同一级别,都是继承了 Collection 接口。LinkedList 实现了 Deque 接口。

3.2、关于 ArrayList 和 LinkedList 的区别

ArrayListLinkedList 都是 List 接口的实现,但它们在内部数据结构和性能上有一些区别:

  1. 内部数据结构:ArrayList 是基于动态数组实现的,支持随机访问,按索引访问元素非常快,时间复杂度为 O(1)。LinkedList 是基于双向链表实现的,不支持高效的随机访问,按索引访问元素需要从头(或尾)开始遍历,时间复杂度为 O(n)。
  2. 插入和删除:ArrayList 的插入和删除操作需要进行数组元素的移动(除非插入和删除操作在列表末尾进行),所以插入和删除元素的时间复杂度为 O(n)。LinkedList 的插入和删除操作只需要改变节点的引用,所以在列表中间插入和删除元素的时间复杂度为 O(1)(前提是已经获取到了要插入位置的节点)。
  3. 内存占用:ArrayList 的内存占用相对较低,因为它只需要存储元素数据和数组的引用。LinkedList 的内存占用较高,因为它需要额外存储节点之间的引用。

总的来说,ArrayList 更适合随机访问场景,LinkedList 更适合插入和删除操作频繁的场景。

3.3、算法:翻转链表

假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。

在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

class Solution {/*** 反转链表。** @param head 链表的头节点* @return 反转后的链表的头节点*/public ListNode reverseList(ListNode head) {// 初始化前一个节点为 nullListNode prev = null;// 初始化当前节点为头节点ListNode curr = head;// 遍历链表直到当前节点为 nullwhile (curr != null) {// 保存当前节点的下一个节点ListNode next = curr.next;// 将当前节点的下一个节点指向前一个节点,完成反转curr.next = prev;// 将前一个节点更新为当前节点prev = curr;// 将当前节点更新为下一个节点curr = next;}// 返回反转后的链表头节点return prev;}
}

4、LinkedList 的使用(常用方法)

下面是对 LinkedList 的常用方法和 Collections 类中的一些涉及 LinkedList 的方法进行详细介绍:

4.1、LinkedList 的常用方法
  1. add(E e)
  • 功能:将元素添加到链表的末尾。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
  1. add(int index, E element)
  • 功能:在指定位置插入元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.add(1, "Orange"); // 将 "Orange" 插入在 "Banana" 前面
  1. remove(Object o)
  • 功能:删除链表中首次出现的指定元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.remove("Apple");
  1. remove(int index)
  • 功能:删除指定索引处的元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.remove(0); // 删除 "Apple"
  1. get(int index)
  • 功能:返回链表中指定位置的元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
String item = list.get(0); // 获取第一个元素 "Apple"
  1. set(int index, E element)
  • 功能:替换指定位置的元素,并返回旧值。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
String oldItem = list.set(0, "Orange"); // 将 "Apple" 替换为 "Orange"
  1. size()
  • 功能:返回链表中的元素数量。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
int size = list.size(); // size 为 1
  1. isEmpty()
  • 功能:检查链表是否为空。
  • 用例:
LinkedList<String> list = new LinkedList<>();
boolean empty = list.isEmpty(); // 判断链表是否为空
  1. clear()
  • 功能:清空链表中的所有元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.clear(); // 清空链表
  1. contains(Object o)
  • 功能:检查链表是否包含指定的元素。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
boolean contains = list.contains("Apple"); // 检查是否包含 "Apple"
  1. indexOf(Object o)lastIndexOf(Object o)
  • 功能:返回指定元素首次出现和最后一次出现的索引位置。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.add("Apple");
int firstIndex = list.indexOf("Apple"); // 返回 0
int lastIndex = list.lastIndexOf("Apple"); // 返回 2
  1. toArray()
  • 功能:将链表中的元素转换为数组。
  • 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
Object[] array = list.toArray(); // 转换为数组
  1. addAll(Collection<? extends E> c)
  • 功能:将指定集合中的所有元素添加到链表的尾部。
  • 用例:
LinkedList<Integer> list = new LinkedList<>();
LinkedList<Integer> newElements = new LinkedList<>(Arrays.asList(1, 2, 3));
list.addAll(newElements);  // 添加多个元素
  1. addAll(int index, Collection<? extends E> c)
  • 功能:将指定集合中的所有元素添加到链表中的指定位置。
  • 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c"));
LinkedList<String> newElements = new LinkedList<>(Arrays.asList("x", "y"));
list.addAll(1, newElements);  // 在索引1的位置添加多个元素
  1. removeAll(Collection<?> c)
  • 功能:从链表中移除指定集合中也存在的所有元素。
  • 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c", "b"));
list.removeAll(Collections.singleton("b"));  // 移除所有 "b"
  1. retainAll(Collection<?> c)
  • 功能:仅保留链表中那些也包含在指定集合中的元素。
  • 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c"));
list.retainAll(Arrays.asList("a", "b"));  // 保留 "a" 和 "b"
  1. containsAll(Collection<?> c)
  • 功能:如果链表包含指定集合中的所有元素,则返回 true
  • 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c"));
boolean contains = list.containsAll(Arrays.asList("a", "b"));  // 检查是否包含 "a" 和 "b"
4.2、继承自 Queue 接口的方法
  1. offer(E e)
  • 功能:将指定的元素添加到队列的尾部。
  • 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
  1. poll()
  • 功能:获取并移除队列的头部元素,如果队列为空,则返回 null
  • 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.poll(); // 获取并移除 "Apple"
  1. remove()
  • 功能:获取并移除队列的头部元素,如果队列为空,则抛出 NoSuchElementException
  • 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.remove(); // 获取并移除 "Apple"
  1. peek()
  • 功能:获取但不移除队列的头部元素,如果队列为空,则返回 null
  • 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.peek(); // 获取但不移除 "Apple"
  1. element()
  • 功能:获取但不移除队列的头部元素,如果队列为空,则抛出 NoSuchElementException
  • 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.element(); // 获取但不移除 "Apple"
4.3、Collections 类中涉及 LinkedList 的常用方法
  1. sort(List<T> list)
  • 功能:对链表进行排序(自然顺序)。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(3, 1, 4, 1, 5));
Collections.sort(list); // 对链表排序
  1. reverse(List<?> list)
  • 功能:反转链表中元素的顺序。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.reverse(list); // 链表变为 [3, 2, 1]
  1. shuffle(List<?> list)
  • 功能:随机打乱链表中元素的顺序。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.shuffle(list); // 打乱链表元素的顺序
  1. binarySearch(List<? extends Comparable<? super T>> list, T key)
  • 功能:在已排序的链表中使用二分查找法查找指定元素的索引。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3, 4, 5));
Collections.sort(list);
int index = Collections.binarySearch(list, 3); // 在链表中查找数字 3 的索引
  1. copy(List<? super T> dest, List<? extends T> src)
  • 功能:将一个链表中的所有元素复制到另一个链表中。
  • 用例:
LinkedList<Integer> src = new LinkedList<>(Arrays.asList(1, 2, 3));
LinkedList<Integer> dest = new LinkedList<>(Arrays.asList(5, 6, 7, 8));
Collections.copy(dest, src); // 将 src 复制到 dest
  1. fill(List<? super T> list, T obj)
  • 功能:使用指定元素替换链表中的所有元素。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.fill(list, 0); // 将链表中的所有元素替换为 0
  1. addAll(Collection<? super T> c, T... elements)
  • 功能:向集合中添加多个元素。
  • 用例:
LinkedList<Integer> list = new LinkedList<>();
Collections.addAll(list, 1, 2, 3, 4);  // 向链表中添加多个元素
  1. replaceAll(List<T> list, T oldVal, T newVal)
  • 功能:替换链表中所有的旧值为新值。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 1, 3));
Collections.replaceAll(list, 1, 99);  // 将所有1替换为99
  1. frequency(Collection<?> c, Object o)
  • 功能:返回指定元素在集合中出现的次数。
  • 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 1, 3));
int freq = Collections.frequency(list, 1);  // 计算1在链表中出现的次数
  1. disjoint(Collection<?> c1, Collection<?> c2)
  • 功能:如果两个集合没有共同的元素,则返回 true。
  • 用例:
LinkedList<Integer> list1 = new LinkedList<>(Arrays.asList(1, 2, 3));
LinkedList<Integer> list2 = new LinkedList<>(Arrays.asList(4, 5, 6));
boolean isDisjoint = Collections.disjoint(list1, list2);  // 检查两个链表是否没有共同元素

通过以上方法,可以对 LinkedList 进行各种常用操作,如添加、删除、获取元素等,以及使用 Collections 类中的方法对 LinkedList 进行排序、反转、打乱顺序等批量操作。特别是继承自 Queue 接口的方法,可以使 LinkedList 作为队列使用,提供了队列相关的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/351744.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Guitar Pro 8软件安装包下载

简介&#xff1a; Guitar Pro吉他软件为帮助所有吉他爱好者学习、绘谱、创作而设计——包含吉他的现有指法及音色&#xff0c; Guitar Pro能了解各类线谱&#xff0c;看谱练吉他&#xff0c;对谱听示范&#xff0c;记录初创声音。 在做弹拨乐器的滑音、倚音、推弦、揉弦、泛…

python图像处理库-PIL(Pillow)

PIL库全称为Python Imaging Library&#xff0c;即Python图像处理库&#xff0c;是一个在Python中用于处理图像的非常流行的库。 一、PIL介绍 这个库提供了广泛的文件格式支持、高效的内部表示以及相当强大的图像处理功能。 核心图像库旨在快速访问存储在几种基本像素格式中的数…

Excel报表

(Apache POI) 入门案例 P164 使用POI需要导入下面2个坐标&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId> </dependency> <dependency><groupId>org.apache.poi</groupId>&…

Python基础教程(二十三):JSON数据解析

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

LabVIEW常用的加密硬件

LabVIEW在工程和科学领域中广泛应用&#xff0c;其中数据保护和程序安全尤为重要。为了确保数据的安全性和完整性&#xff0c;常用的加密硬件设备包括TPM&#xff08;可信平台模块&#xff09;、HSM&#xff08;硬件安全模块&#xff09;和专用加密芯片。本文将推荐几款常用的加…

部署大模型LLM

在autodl上部署大模型 windows运行太麻烦&#xff0c;环境是最大问题。 选择云上服务器【西北B区 / 514机】 cpp (c c plus plus) 纯 C/C 实现&#xff0c;无需外部依赖。针对使用 ARM NEON、Accelerate 和 Metal 框架的 Apple 芯片进行了优化。支持适用于 x86 架构的 AVX、…

猎食者优化算法 Python代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类算法的家人&#xff0c;可关注我的VX公众号&#xff1a;python算法小当家&#xff0c;不定期会有很多免费代码分享~ 猎食者优化算法 Python代码免费获取 猎食者优化算法(hunter–…

基于GTX的64B66B编码IP生成(高速收发器二十)

点击进入高速收发器系列文章导航界面 1、配置GTX IP 相关参数 前文讲解了64B66B编码解码原理&#xff0c;以及GTX IP实现64B66B编解码的相关信号组成&#xff0c;本文生成64B66B编码的GTX IP。 首先如下图所示&#xff0c;需要对GTX共享逻辑进行设置&#xff0c;为了便于扩展&a…

什么是微前端

什么是微前端&#xff1f; 微前端 这个名词&#xff0c;第一次被提出还是在2016年底&#xff0c;那是在 ThoughtWorks Technology Radar。这个概念将微服务这个被广泛应用于服务端的技术范式扩展到前端领域。现代的前端应用的发展趋势正在变得越来越富功能化&#xff0c;富交互…

RPG游戏完整指南

环境&#xff1a;unity2021urp 本教程教大家如何使用Unity创建一个RPG游戏&#xff0c;玩家可以在城镇场景中进行导航并寻找战斗&#xff0c;并在战斗中遇到不同类型的敌人。玩家可以向敌人施加不同的动作&#xff0c;如&#xff1a;常规攻击和撤离。这会是一个十分有趣的体验。…

[next.js] svgr/webpack

nextjs如何配置svg文件&#xff0c;使其像react组件一样导入? 当前next.js 开发环境我使用了--turbo 来开启turbopack加速文件构建&#xff0c;所以之前的一些webpack loader之类的无法正常工作。通过搜索发现一般都是使用svgr/webpack来处理svg&#xff0c;打开svgr官网发现…

Linux初识地址空间

前言 上一期我们对进程优先级、命令行参数以及环境和变量做了介绍&#xff01;以前我们就提到过一个问题有了运行队列为什么还要有优先级&#xff1f;本期将带你揭晓&#xff01; 本期内容介绍 虚拟地址空间的引入 虚拟地址空间的介绍 如何理解地址空间 为什么要有地址空间 如…

docker和docker compose 部署

一. 将微服务运行在docker上&#xff1a; 1.新建一个空文件夹docker-demo&#xff0c;在里面再新建文件夹app&#xff0c;在app目录下新建一个名为Dockerfile的文件。 2.编写Dockerfile文件 3.构建镜像 4.启动镜像 5.可以访问了。 二使用Dockerfile构建微服务镜像 1.将j…

数据库系统概念(第八周 第一堂)(规范化关系数据库设计)(强推学习!!!)

目录 前言 E-R模型质量低的深层原因 数据依赖 函数依赖 主属性/非主属性 逻辑蕴含与闭包 Armstrongs Axiom 求解F闭包算法 求解属性集闭包算法 属性集闭包的作用 候选码求解理论和算法 候选码求解理论 无关属性 检验方法 正则覆盖 关系模式的设计 关系…

Spark常见的可以优化的点

Shuffle 复用 # 1.以下操作会复用的shuffle结果&#xff0c;只会读一遍数据源 val rdd1 sc.textFile("hdfs://zjyprc-hadoop/tmp/hive-site.xml").flatMap(_.split(" ")).map(x > (x,1)).reduceByKey(_ _).filter(_._2 > 1) rdd1.count() rdd1.fil…

Python高级用法:路径与文件操作(os与pathlib)

路径与文件 前言导入包判断路径存在判断路径类型&#xff08;判断文件还是文件夹&#xff09;获取父路径写入读出文件获得路径中所有子文件/子文件夹获取文件扩展名获取多个文件扩展名获取路径的组件创建目录删除文件或空目录 前言 在Python中&#xff0c;os模块提供了与操作系…

美国裸机云站群服务器使用指南

在当今数字化时代&#xff0c;网站和应用程序的稳定运行对于企业和个人都至关重要。为了满足日益增长的业务需求&#xff0c;裸机云站群服务器成为了一个理想的选择。以下是美国裸机云站群服务器的使用指南&#xff0c;帮助您更好地利用这一强大的云服务。 一、选择信誉良好的云…

AI大模型在运动项目的深度融合和在穿戴设备的实践及未来运动健康技术发展

文章目录 1. 技术架构2. 模型选择2.1 LSTM&#xff08;长短期记忆网络&#xff09;2.2 CNN&#xff08;卷积神经网络&#xff09;2.3 Transformer 3. 数据处理数据预处理 4. 实时性要求4.1 边缘计算4.2 模型优化 5. 数据隐私与安全6. 深入分析AI大模型在穿戴设备的应用和未来发…

k8s中的pod域名解析失败定位案例

问题描述 我在k8s中启动了一个Host网络模式的pod&#xff0c;这个pod的域名解析失败了。 定位步骤 敲kubectl exec -it [pod_name] -- bash进入pod后台&#xff0c;查看/etc/resolv.conf&#xff0c;发现nameserver配的有问题。这里我预期的nameserver应该使用宿主机的&…

动态 SQL

动态 SQL 是 MyBatis 的强大特性之一&#xff0c;能够完成不同条件下不同的 sql 拼接。也就是说执行的 SQL 语句并不是固定的&#xff0c;而是不同人的不同操作执行的语句会有所差异。MyBatis 通过使用 标签 的方式来实现这种灵活性的。 <if>标签 例如在有一些网站进行…