当前位置: 首页 > news >正文

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 作为队列使用,提供了队列相关的操作。

相关文章:

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

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 014 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…...

【Ruby爬虫01】某吃瓜网站图片数据采集

介绍 由于最近在学习Ruby&#xff0c;写一个爬虫锻炼一下。涉及xml解析、多线程、xpath语法等基础知识。 实现代码 使用说明 使用前请先安装如下gem gem install nokogiri http openssl# nokogiri&#xff1a;一个解析xml和html的库&#xff0c;支持css、xpath语法 # htt…...

可以免费领取tokens的大模型服务

本文更新时间&#xff1a;2024年6月20日 豆包大模型 “亲爱的客户&#xff0c;模型提供方将在5月15日至8月30日期间&#xff0c;为您提供一次独特的机会&#xff0c;即高达5亿tokens的免费权益。这是我们对您长期支持的感谢&#xff0c;也是对未来合作的期待。” 在8月30日之…...

NSSCTF-Web题目11

目录 [鹤城杯 2021]EasyP 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]numgame 1、题目 2、知识点 3、思路 [鹤城杯 2021]EasyP 1、题目 2、知识点 php代码审计 3、思路 打开题目&#xff0c;出现一段代码&#xff0c;我们对代码进行审计 这里出现了很多不懂的…...

【数据结构】第十八弹---C语言实现堆排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、堆排序 1.1、基本思想 1.2、初步代码实现 1.3、代码优化 1.4、代码测试 总结 1、堆排序 在博主数据结构第十二弹---堆的应用有详细讲解堆…...

[面试题]Kafka

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL[面试题]Maven[面试题]Spring Boot[面试题]Spring Cloud[面试题]Spring MVC[面试题]Spring[面试题]MyBatis[面试题]Nginx[面试题]缓存[面试题]Redis[面试题]消息队列[面试题]…...

centos7 离线安装zip和unzip

解压的时候发现不能解压&#xff0c;报-bash: unzip: command not found 1、访问https://www.rpmfind.net/linux/rpm2html/search.php?queryzip&submitSearch…&systemcentos&arch#/ 2、输入zip和centos搜索&#xff0c;选择el7下载 3、输入unzip和centos搜索&am…...

Linux下lsof命令使用

目录 lsof 命令使用指南基本语法常用选项使用示例 lsof vs netstatlsofnetstat区别示例对比 lsof 命令使用指南 lsof (List Open Files) 是一个用于列出当前系统中打开文件的命令&#xff0c;适用于 Unix 和类 Unix 操作系统。它不仅可以列出常规文件&#xff0c;还可以列出打…...

基于ChatGPT的大型语言模型试用心得

近年来&#xff0c;ChatGPT这样的大型语言模型&#xff0c;它如同一颗冉冉升起的新星&#xff0c;迅速在商业、教育、娱乐等多个领域照亮了创新的天空&#xff0c;极大地革新了我们的工作与日常生活。 最近我发现一些国内用户也能自由访问的中文ChatGPT APP。这个平台不仅提供…...

Python 列表添加多个值(四种方法)

Python 列表添加多个值有多种方法,以下是其中几种实现方法: 一、使用extend()方法 Python 中列表对象有一个 extend() 方法,它可以一次性添加另一个列表中的所有元素到当前列表中。 例1: a = [1, 2, 3] b = [4, 5, 6] a.extend(b)...

VMware RedHat虚拟机磁盘扩容(添加磁盘和扩展磁盘)

前言 自己的电脑上配一个虚拟机还是很有必要的&#xff0c;用起来比双系统方便一点&#xff0c;之前搞了100g的ubuntu没用到&#xff0c;后面重装redhat觉得随便搞个20g就够用了&#xff0c;后面用到之后就遇到磁盘不够用的情况&#xff0c;只能说情况允许的话&#xff0c;磁盘…...

最近,GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价,包括版本间的对比分析、GPT-4o的技术能力以及个人整体感受等

GPT-4o是一款引人瞩目的人工智能技术&#xff0c;它在之前版本的基础上取得了长足的进步。本文将对GPT-4o进行评价&#xff0c;包括版本间的对比分析、GPT-4o的技术能力以及个人整体感受等。 首先&#xff0c;我们来进行GPT-4o与之前版本的对比分析。GPT-4o相较于GPT-3和GPT-2…...

C#面:C#支持多重继承么?

C#不支持多重继承。在C#中&#xff0c;一个类只能直接继承自一个基类。这是由于C#的设计目标之一是避免多重继承可能带来的复杂性和潜在的问题。 然而&#xff0c;C#提供了接口&#xff08;interface&#xff09;的概念来实现类似多重继承的功能。一个类可以实现多个接口&…...

细说MCU修改回调函数调用模式的方法

目录 1、硬件及工程 2、实现方法 &#xff08;1&#xff09;修改while(1)中的代码&#xff1a; &#xff08;2&#xff09;修改2 &#xff08;3&#xff09;修改3 &#xff08;4&#xff09;修改4 &#xff08;5&#xff09;修改5 3、下载并运行 在本文作者的文章中&a…...

Java共享台球室无人系统支持微信小程序+微信公众号

共享台球室无人系统 &#x1f3b1; 创新台球体验 近年来&#xff0c;共享经济如火如荼&#xff0c;从共享单车到共享汽车&#xff0c;无一不改变着我们的生活方式。而如今&#xff0c;这一模式已经渗透到了更多领域&#xff0c;共享台球室便是其中之一。不同于传统的台球室&a…...

如何开发一个海外仓系统?难度在哪,怎么选择高性价解决方案

作为海外仓管理的重要工具&#xff0c;海外仓系统的实际应用价值还是非常高的。为了让大家能更好的理解wms海外仓系统&#xff0c;今天会介绍海外仓系统开发的逻辑架构&#xff0c;以及作为海外仓企业要怎么确定高性价比的数字化管理解决方案。 1、开发海外仓系统要考虑的功能…...

计算机组成原理(Wrong Question)

目录 一、计算机系统概述 *1.1 计算机发展历程 1.2 计算机系统层次结构 1.3 计算机的性能指标 二、 数据的表示和运算 2.1 数制和编码 2.2 运算方法和运算电路 2.3 浮点数的表示与运算 三、存储系统 3.1 存储器概述 3.2 主存储器 3.3 主存储器与CPU的连接 3.4 外部…...

ACL2024 | AI的时空穿越记:大型语言模型共时推理的奇幻之旅!

作者&#xff1a;苏肇辰 标题&#xff1a;Living in the Moment: Can Large Language Models Grasp Co-Temporal Reasoning? 录取&#xff1a;ACL2024 Main 论文链接&#xff1a;https://arxiv.org/abs/2406.09072 代码链接&#xff1a;https://github.com/zhaochen0110/Cotem…...

从xxl-job源码中学习Netty的使用

1. 启动与Spring实例化 com.xxl.job.core.executor.impl.XxlJobSpringExecutor.java类 继承SmartInitializingSingleton 类&#xff0c;在afterSingletonsInstantiated 实例化后方法中 调用initJobHandlerMethodRepository 把所有的xxljob任务管理起来&#xff1b; private…...

人工智能发展历程了解和Tensorflow基础开发环境构建

目录 人工智能的三次浪潮 开发环境介绍 Anaconda Anaconda的下载和安装 下载说明 安装指导 模块介绍 使用Anaconda Navigator Home界面介绍 Environment界面介绍 使用Jupter Notebook 打开Jupter Notebook 配置默认目录 新建文件 两种输入模式 Conda 虚拟环境 添…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...