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):也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。
这和我们日常生活中的排队是一致的,最早排队的也是最早离队的。其操作的特性是先进先出(First In First Out, FIFO),故又称为先进先出的线性表。基本上,一个队列就是一个先入先出(FIFO)的数据结构
在Java 中 Queue 接口与 List、Set 同一级别,都是继承了 Collection 接口。LinkedList 实现了 Deque 接口。
3.2、关于 ArrayList 和 LinkedList 的区别
ArrayList
和 LinkedList
都是 List
接口的实现,但它们在内部数据结构和性能上有一些区别:
- 内部数据结构:
ArrayList
是基于动态数组实现的,支持随机访问,按索引访问元素非常快,时间复杂度为 O(1)。LinkedList
是基于双向链表实现的,不支持高效的随机访问,按索引访问元素需要从头(或尾)开始遍历,时间复杂度为 O(n)。 - 插入和删除:
ArrayList
的插入和删除操作需要进行数组元素的移动(除非插入和删除操作在列表末尾进行),所以插入和删除元素的时间复杂度为 O(n)。LinkedList
的插入和删除操作只需要改变节点的引用,所以在列表中间插入和删除元素的时间复杂度为 O(1)(前提是已经获取到了要插入位置的节点)。 - 内存占用:
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 的常用方法
add(E e)
- 功能:将元素添加到链表的末尾。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
add(int index, E element)
- 功能:在指定位置插入元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.add(1, "Orange"); // 将 "Orange" 插入在 "Banana" 前面
remove(Object o)
- 功能:删除链表中首次出现的指定元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.remove("Apple");
remove(int index)
- 功能:删除指定索引处的元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.add("Banana");
list.remove(0); // 删除 "Apple"
get(int index)
- 功能:返回链表中指定位置的元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
String item = list.get(0); // 获取第一个元素 "Apple"
set(int index, E element)
- 功能:替换指定位置的元素,并返回旧值。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
String oldItem = list.set(0, "Orange"); // 将 "Apple" 替换为 "Orange"
size()
- 功能:返回链表中的元素数量。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
int size = list.size(); // size 为 1
isEmpty()
- 功能:检查链表是否为空。
- 用例:
LinkedList<String> list = new LinkedList<>();
boolean empty = list.isEmpty(); // 判断链表是否为空
clear()
- 功能:清空链表中的所有元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
list.clear(); // 清空链表
contains(Object o)
- 功能:检查链表是否包含指定的元素。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
boolean contains = list.contains("Apple"); // 检查是否包含 "Apple"
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
toArray()
- 功能:将链表中的元素转换为数组。
- 用例:
LinkedList<String> list = new LinkedList<>();
list.add("Apple");
Object[] array = list.toArray(); // 转换为数组
addAll(Collection<? extends E> c)
- 功能:将指定集合中的所有元素添加到链表的尾部。
- 用例:
LinkedList<Integer> list = new LinkedList<>();
LinkedList<Integer> newElements = new LinkedList<>(Arrays.asList(1, 2, 3));
list.addAll(newElements); // 添加多个元素
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的位置添加多个元素
removeAll(Collection<?> c)
- 功能:从链表中移除指定集合中也存在的所有元素。
- 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c", "b"));
list.removeAll(Collections.singleton("b")); // 移除所有 "b"
retainAll(Collection<?> c)
- 功能:仅保留链表中那些也包含在指定集合中的元素。
- 用例:
LinkedList<String> list = new LinkedList<>(Arrays.asList("a", "b", "c"));
list.retainAll(Arrays.asList("a", "b")); // 保留 "a" 和 "b"
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
接口的方法
offer(E e)
- 功能:将指定的元素添加到队列的尾部。
- 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
poll()
- 功能:获取并移除队列的头部元素,如果队列为空,则返回
null
。 - 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.poll(); // 获取并移除 "Apple"
remove()
- 功能:获取并移除队列的头部元素,如果队列为空,则抛出
NoSuchElementException
。 - 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.remove(); // 获取并移除 "Apple"
peek()
- 功能:获取但不移除队列的头部元素,如果队列为空,则返回
null
。 - 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.peek(); // 获取但不移除 "Apple"
element()
- 功能:获取但不移除队列的头部元素,如果队列为空,则抛出
NoSuchElementException
。 - 用例:
LinkedList<String> queue = new LinkedList<>();
queue.offer("Apple");
String item = queue.element(); // 获取但不移除 "Apple"
4.3、Collections 类中涉及 LinkedList 的常用方法
sort(List<T> list)
- 功能:对链表进行排序(自然顺序)。
- 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(3, 1, 4, 1, 5));
Collections.sort(list); // 对链表排序
reverse(List<?> list)
- 功能:反转链表中元素的顺序。
- 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.reverse(list); // 链表变为 [3, 2, 1]
shuffle(List<?> list)
- 功能:随机打乱链表中元素的顺序。
- 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.shuffle(list); // 打乱链表元素的顺序
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 的索引
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
fill(List<? super T> list, T obj)
- 功能:使用指定元素替换链表中的所有元素。
- 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 3));
Collections.fill(list, 0); // 将链表中的所有元素替换为 0
addAll(Collection<? super T> c, T... elements)
- 功能:向集合中添加多个元素。
- 用例:
LinkedList<Integer> list = new LinkedList<>();
Collections.addAll(list, 1, 2, 3, 4); // 向链表中添加多个元素
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
frequency(Collection<?> c, Object o)
- 功能:返回指定元素在集合中出现的次数。
- 用例:
LinkedList<Integer> list = new LinkedList<>(Arrays.asList(1, 2, 1, 3));
int freq = Collections.frequency(list, 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 的介绍、使用、原理与源码解析
大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 014 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进…...
【Ruby爬虫01】某吃瓜网站图片数据采集
介绍 由于最近在学习Ruby,写一个爬虫锻炼一下。涉及xml解析、多线程、xpath语法等基础知识。 实现代码 使用说明 使用前请先安装如下gem gem install nokogiri http openssl# nokogiri:一个解析xml和html的库,支持css、xpath语法 # htt…...
可以免费领取tokens的大模型服务
本文更新时间:2024年6月20日 豆包大模型 “亲爱的客户,模型提供方将在5月15日至8月30日期间,为您提供一次独特的机会,即高达5亿tokens的免费权益。这是我们对您长期支持的感谢,也是对未来合作的期待。” 在8月30日之…...
NSSCTF-Web题目11
目录 [鹤城杯 2021]EasyP 1、题目 2、知识点 3、思路 [SWPUCTF 2022 新生赛]numgame 1、题目 2、知识点 3、思路 [鹤城杯 2021]EasyP 1、题目 2、知识点 php代码审计 3、思路 打开题目,出现一段代码,我们对代码进行审计 这里出现了很多不懂的…...
【数据结构】第十八弹---C语言实现堆排序
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【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
解压的时候发现不能解压,报-bash: unzip: command not found 1、访问https://www.rpmfind.net/linux/rpm2html/search.php?queryzip&submitSearch…&systemcentos&arch#/ 2、输入zip和centos搜索,选择el7下载 3、输入unzip和centos搜索&am…...
Linux下lsof命令使用
目录 lsof 命令使用指南基本语法常用选项使用示例 lsof vs netstatlsofnetstat区别示例对比 lsof 命令使用指南 lsof (List Open Files) 是一个用于列出当前系统中打开文件的命令,适用于 Unix 和类 Unix 操作系统。它不仅可以列出常规文件,还可以列出打…...
基于ChatGPT的大型语言模型试用心得
近年来,ChatGPT这样的大型语言模型,它如同一颗冉冉升起的新星,迅速在商业、教育、娱乐等多个领域照亮了创新的天空,极大地革新了我们的工作与日常生活。 最近我发现一些国内用户也能自由访问的中文ChatGPT APP。这个平台不仅提供…...
Python 列表添加多个值(四种方法)
Python 列表添加多个值有多种方法,以下是其中几种实现方法: 一、使用extend()方法 Python 中列表对象有一个 extend() 方法,它可以一次性添加另一个列表中的所有元素到当前列表中。 例1: a = [1, 2, 3] b = [4, 5, 6] a.extend(b)...
VMware RedHat虚拟机磁盘扩容(添加磁盘和扩展磁盘)
前言 自己的电脑上配一个虚拟机还是很有必要的,用起来比双系统方便一点,之前搞了100g的ubuntu没用到,后面重装redhat觉得随便搞个20g就够用了,后面用到之后就遇到磁盘不够用的情况,只能说情况允许的话,磁盘…...
最近,GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价,包括版本间的对比分析、GPT-4o的技术能力以及个人整体感受等
GPT-4o是一款引人瞩目的人工智能技术,它在之前版本的基础上取得了长足的进步。本文将对GPT-4o进行评价,包括版本间的对比分析、GPT-4o的技术能力以及个人整体感受等。 首先,我们来进行GPT-4o与之前版本的对比分析。GPT-4o相较于GPT-3和GPT-2…...
C#面:C#支持多重继承么?
C#不支持多重继承。在C#中,一个类只能直接继承自一个基类。这是由于C#的设计目标之一是避免多重继承可能带来的复杂性和潜在的问题。 然而,C#提供了接口(interface)的概念来实现类似多重继承的功能。一个类可以实现多个接口&…...
细说MCU修改回调函数调用模式的方法
目录 1、硬件及工程 2、实现方法 (1)修改while(1)中的代码: (2)修改2 (3)修改3 (4)修改4 (5)修改5 3、下载并运行 在本文作者的文章中&a…...
Java共享台球室无人系统支持微信小程序+微信公众号
共享台球室无人系统 🎱 创新台球体验 近年来,共享经济如火如荼,从共享单车到共享汽车,无一不改变着我们的生活方式。而如今,这一模式已经渗透到了更多领域,共享台球室便是其中之一。不同于传统的台球室&a…...
如何开发一个海外仓系统?难度在哪,怎么选择高性价解决方案
作为海外仓管理的重要工具,海外仓系统的实际应用价值还是非常高的。为了让大家能更好的理解wms海外仓系统,今天会介绍海外仓系统开发的逻辑架构,以及作为海外仓企业要怎么确定高性价比的数字化管理解决方案。 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的时空穿越记:大型语言模型共时推理的奇幻之旅!
作者:苏肇辰 标题:Living in the Moment: Can Large Language Models Grasp Co-Temporal Reasoning? 录取:ACL2024 Main 论文链接:https://arxiv.org/abs/2406.09072 代码链接:https://github.com/zhaochen0110/Cotem…...
从xxl-job源码中学习Netty的使用
1. 启动与Spring实例化 com.xxl.job.core.executor.impl.XxlJobSpringExecutor.java类 继承SmartInitializingSingleton 类,在afterSingletonsInstantiated 实例化后方法中 调用initJobHandlerMethodRepository 把所有的xxljob任务管理起来; private…...
人工智能发展历程了解和Tensorflow基础开发环境构建
目录 人工智能的三次浪潮 开发环境介绍 Anaconda Anaconda的下载和安装 下载说明 安装指导 模块介绍 使用Anaconda Navigator Home界面介绍 Environment界面介绍 使用Jupter Notebook 打开Jupter Notebook 配置默认目录 新建文件 两种输入模式 Conda 虚拟环境 添…...
makefile追加warning日志
在Makefile中,你不能直接“追加”warning日志到构建过程中,但你可以通过几种方式在构建时产生额外的警告或消息。以下是一些常用的方法: 使用echo或printf命令: 在Makefile的规则中,你可以使用echo或printf命令来输出警…...
不要直接使用unidefined 而使用void 0
为什么不要使用unidefined 而使用void 0? 在JavaScript中,undefined 和 void 0 都可以用来表示未定义的值,但它们在使用和上下文中有一些微妙的差异,这也是为什么有时可能会推荐使用 void 0 而不是直接使用 undefined。 全局污染ÿ…...
注解详解系列 - @Scope:Bean作用域管理
注解简介 在今天的注解详解系列中,我们将探讨Scope注解。Scope是Spring框架中的一个重要注解,用于定义Spring bean的作用域。通过指定bean的作用域,我们可以控制bean的生命周期和创建方式。 注解定义 Scope注解用于指定Spring bean的作用域…...
数学建模基础:数学建模概述
目录 前言 一、数学建模的步骤 二、模型的分类 三、模型评价指标 四、常见的数学建模方法 实际案例:线性回归建模 步骤 1:导入数据 步骤 2:数据预处理 步骤 3:建立线性回归模型 步骤 4:模型验证 步骤 5&…...
人工智能大模型之开源大语言模型汇总(国内外开源项目模型汇总)
开源大语言模型完整列表 Large Language Model (LLM) 即大规模语言模型,是一种基于深度学习的自然语言处理模型,它能够学习到自然语言的语法和语义,从而可以生成人类可读的文本。 所谓"语言模型",就是只用来处理语言文…...
数据结构之B树
引言 在计算机科学中,数据结构是用于组织和存储数据的关键工具。其中,B树(B-tree)作为一种自平衡的树形数据结构,被广泛应用于数据库和文件系统中,以提高查找、插入、删除和范围查询的效率。本文将深入探讨…...
双色球预测算法(Java),——森林机器学习、时间序列
最近AI很火,老想着利用AI的什么算法,干点什么有意义的事情。其中之一便想到了双色球,然后让AI给我预测,结果基本都是简单使用随机算法列出了几个数字。 额,,,,咋说呢,双…...
【计算机网络篇】数据链路层(11)在数据链路层扩展以太网
文章目录 🍔使用网桥在数据链路层扩展以太网🥚网桥的主要结构和基本工作原理🎈网桥的主要结构🔎网桥转发帧的例子🔎网桥丢弃帧的例子🔎网桥转发广播帧的例子 🥚透明网桥🔎透明网桥的…...
Ubuntu20.04 使用scrapy-splash爬取动态网页
我们要先安装splash服务,使用dock安装,如果dock没有安装,请参考我的上一篇博文: 按照官方文档:https://splash.readthedocs.io/en/stable/install.html 1.下载splash sudo docker pull scrapinghub/splash2.安装scrapy…...
Function:控制继电器上下电,上电后adb登录,copy配置文件
import serial import time import datetime import subprocess import osdef append_to_txt(file_path, content):if os.path.exists(file_path):with open(file_path, a) as file: # 使用 a 模式打开文件进行追加file.write(content \n) # 追加内容,并换行else…...
杭州市网站制作/什么是外链
有时候我们在使用pycharm编写python代码的时候,发现没有代码提示,怎么解决呢,下面来分享一下方法 工具/原料 pycharm 没有代码提示解决方法 方法一:检查是否关闭代码提示 1 第一步在我们的电脑上打开pycharm,输入代…...
动态表情包在线制作网站/网络宣传的方法有哪些
相对中资公司来说,美国或者西方公司的面试流程更加复杂,考核的内容更多。 不管是那种面试,很多时候求职者会遇到:面着面着就没了。 当然我们这说的只是你应聘中层以下的技术或者部分管理岗。 如果你是应聘公司高层,…...
现在哪些做进口商品的电商网站/百度网盘下载app
配置本地tomcat服务器时间查看时间 内容精选换一换弹性云服务器显示的Windows操作系统时间与本地标准时间不一致。系统时间由于受到网络或一些进程驱动的影响可能会出现和标准时间不一致的情况。手动同步系统时间。单击桌面右下角的“更改日期和时间设置”,打开“日…...
通过网站开发工具怎么改自动跳网站/百度推广和百度竞价有什么区别
网站运营指标统计案例-★★★★★数据需求代码实现数据 我们后续需要关注的数据 1.总共有多少条?2.不重复的IP有多少?3.来源URL字段计数并排序 需求 需求1.PV:PageView ,页面访问量, 访问一次就是一个PV,上面的访问日志有多少条就有多少PV 分析:读取文件,把该RDD直接count…...
成都专门做公司网站的公司/成人职业技能培训有哪些项目
1,安装ubuntu双系统教程参看 http://www.jianshu.com/p/2eebd6ad284d 简书教程; 注意安装过程中一些小问题。 1)给ubuntu分区时候,分完四个区后 install出现问题,遮个教程没有涉及; 错误提示要为biosgrub再分个》1M的区&#…...
做三轨网站犯法吗/常熟网站建设
1.burnside定理,polya计数法 这个大家可以看brudildi的《组合数学》,那本书的这一章写的很详细也很容易理解。最好能完全看懂了,理解了再去做题,不要只记个公式。 *简单题:(直接用套公式就可以了&…...