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

news2024/10/5 21:23:02

大家好,我是栗筝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
     */
    @Override
    public boolean add(E e) {
      	// 在链表末尾链接一个新的节点
        linkLast(e);
        return true;
    }
  
    /**
     * 在链表末尾链接一个新的节点
     *
     * @param e 新节点存储的元素
     */
    void linkLast(E e) {
        // 保存当前链表的最后一个节点
        final Node<E> l = last;
        // 创建一个新的节点,前驱节点是当前最后一个节点,后继节点是 null
        final Node<E> newNode = new Node<>(l, e, null);
        // 将链表的最后一个节点更新为新节点
        last = newNode;
        if (l == null)
            // 如果链表之前是空的,即没有任何元素
            // 将新节点设置为链表的第一个节点
            first = newNode;
        else
            // 如果链表中已有元素
            // 将原最后一个节点的 next 指针指向新节点
            l.next = newNode;
      	// 链表大小增加 1
        size++;  
    }
  
    /**
     * 在指定索引处插入元素
     *
     * @param index 要插入元素的位置
     * @param element 要插入的元素
     */
    public void add(int index, E element) {
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);
        else
            linkBefore(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,后继是 succ
        final 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;
        // 链表大小增加 1
        size++;
        // 修改计数器增加 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;
        // 链表大小减 1
        size--;
        // 修改计数器增加 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) {
        // 如果要移除的元素为 null
        if (o == null) { 
            for (Node<E> x = first; x != null; x = x.next) {
                // 从链表的第一个节点开始遍历
                if (x.item == null) {
                    // 如果当前节点的元素为 null
                    unlink(x);  
                    // 解除并移除当前节点
                    // 返回 true,表示移除成功
                    return true;  
                }
            }
        } else {  
            // 如果要移除的元素不为 null
            for (Node<E> x = first; x != null; x = x.next) {  
                // 从链表的第一个节点开始遍历
                // 如果当前节点的元素等于要移除的元素
                if (o.equals(x.item)) {
                    // 解除并移除当前节点
                    unlink(x);
                    // 返回 true,表示移除成功
                    return true;  
                }
            }
        }
        // 如果没有找到要移除的元素,返回 false
        return 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 节点的元素设置为新元素 e
            lastReturned.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;
        // 如果链表为空,抛出 NoSuchElementException
        if (f == null)
            throw new NoSuchElementException();
        // 移除并返回第一个元素
        return unlinkFirst(f);
    }
  
    /**
     * 获取但不移除此队列的头部;如果队列为空,则返回 null。
     *
     * @return 队列头部的元素,如果队列为空则返回 null
     */
    public E peek() {
        // 保存链表的第一个节点
        final Node<E> f = first;
        // 返回第一个元素但不移除,如果链表为空返回 null
        return (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;
        // 如果链表为空,抛出 NoSuchElementException
        if (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) {
            // 更新链表的尾节点为 null
            last = null;
        } else {
            // 将新的头节点的前驱引用设为 null
            next.prev = null;
        }
        // 链表大小减 1
        size--;
        // 修改计数器增加 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) {
        // 初始化前一个节点为 null
        ListNode prev = null;
        // 初始化当前节点为头节点
        ListNode curr = head;
        // 遍历链表直到当前节点为 null
        while (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.coloradmin.cn/o/1830352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【漏洞复现】畅捷通T+ App_Code.ashx 远程命令执行漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

什么是加密算法,有什么不同类型?

加密算法是一种数学函数或程序&#xff0c;它能够将原始的、可读的数据&#xff08;也称为“明文”&#xff09;转换为一种不可读的代码形式&#xff08;称为“密文”&#xff09;。这种转换是通过特定的算法和密钥来实现的&#xff0c;目的是保护数据的机密性和完整性&#xf…

HTML表单深度解析:构建互动的网页界面

表单是HTML中用于收集用户输入信息的重要元素&#xff0c;是网页与用户交互的关键组件。以下是一个典型的HTML表单示例&#xff0c;我们将会详细解析其中的各个元素及属性含义。 <form action"https://xx.xxx.xx/search" target"_self" method"ge…

CDN简介

CDN 的基本概念 CDN&#xff08;Content Delivery Network&#xff09;&#xff0c;即内容分发网络。 CDN是一种分布式网络架构&#xff1a;它由分布在不同地理位置的服务器组成网络&#xff0c;这些服务器协同工作以提供内容服务。 内容分发的核心目标 确保用户能够快速、可…

WordPress管理员后台登录地址修改教程,WordPress admin登录地址文件修改方法

我们使用WordPress时&#xff0c;管理员后台登录默认地址为“域名/wp-login.php”或“域名/wp-admin”&#xff0c;为了安全&#xff0c;一般会把此地址改掉&#xff0c;防止有人恶意来攻击咱的WordPress&#xff0c;今天出个WordPress后台登录地址修改教程&#xff0c;修改之后…

[oeasy]python0021_宝剑镶宝石_爱之石中剑_批量替换_特殊字符_特殊颜色

继续运行 &#x1f94b; 回忆上次内容 上次 运行了 game.py分析了 game.py也大致读懂了 game.py 这个 程序 可以进一步 进行修改吗&#xff1f;&#xff1f; 添加爱心 可以 把这个 ❤ 选中并复制 再粘贴到 虚拟机右侧的 剪贴板 然后 回到 游戏程序 进行修改和粘贴 按方向键h…

2024广州光亚展参展记录

参展总结 智控面板外观设计百家齐放&#xff0c;但始终逃不出几大设计元素的组合&#xff08;各种尺寸的屏、不同规则的按键切分、不同材质的面板材质&#xff09;&#xff1b;互联互通的趋势明显&#xff0c;接入米家、小度、涂鸦、HomeKit平台成为众多厂商的首选&#xff1b;…

JavaFX BorderPane布局

BorderPane布局顶部&#xff0c;底部&#xff0c;左&#xff0c;右或中心区域中的子节点。每个区域只能有一个节点。BorderPane的顶部和底部区域允许可调整大小的节点占用所有可用宽度。 左边界区域和右边界区域占据顶部和底部边界之间的可用垂直空间。 默认情况下&#xff0c…

一二三应用开发平台应用开发示例(3)——生成库表及后端代码

生成库表 前端页面的配置&#xff0c;也就是视图功能&#xff0c;我们先放一放&#xff0c;来看看生成库表和后端代码。 关闭实体配置界面&#xff0c;回到实体列表&#xff0c;勾选“文件夹”实体&#xff0c;点击“生成库表”&#xff0c;并确定。 系统提示成功后&#xff…

【Linux应用】Linux系统的设备管理——Udev

1.udev概述 udev是 Linux2.6内核里的一个功能&#xff0c;它替代了原来的 devfs&#xff0c;成为当前 Linux 默认的设备管理工具&#xff0c;能够根据系统中的硬件设备的状态动态更新设备文件&#xff0c;包括设备文件的创建&#xff0c;删除等。 udev以守护进程的形式运行&am…

python基础 002 - 1 基础语法

1 标识符&#xff08;identifier&#xff09;&#xff0c;识别码&#xff0c;表明身份 身份证&#xff0c;ID 定义&#xff1a;在编程语言中标识符就是程序员自己规定的具有特定含义的词&#xff0c;比如类名称、属性名称、变量名等&#xff0c; 在Python 中&#xff0c;pyt…

教学资源共享平台的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;老师管理&#xff0c;用户管理&#xff0c;成绩管理&#xff0c;教学资源管理&#xff0c;作业管理 老师账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用…

数组元素的内存地址计算【数据结构与算法C#版】

数组元素被存储在连续的内存空间中&#xff0c;这意味着计算数组元素的内存地址非常容易。给定数组内存地址&#xff08;首 元素内存地址&#xff09;和某个元素的索引&#xff0c;我们可以使用下方图 所示的公式计算得到该元素的内存地址&#xff0c;从而直接 访问该元素。 观…

python数据分析--- ch12-13 python参数估计与假设检验

python数据分析--- ch12-13 python参数估计与假设检验 1. Ch12--python 参数估计1.1 参数估计与置信区间的含义及函数版1.1.1 参数估计与置信区间的含义1.1.2 参数估计函数版1.1.3 参数估计函数版 1.2 Python单正态总体均值区间估计1.2.1 方差 σ 2 \sigma^2 σ2已知1.2.2 方差…

在 Blazor WebAssembly 中使用 EF Core 7 进行 CRUD 操作

如今&#xff0c;作为一名开发人员&#xff0c;如果我们想开发任何基于 Web 的应用程序&#xff0c;我们可以通过多种方式开发它们。现在&#xff0c;我们有几种选项来构建任何基于 Web 的应用程序&#xff0c;例如 MVC 框架、基于 API 的结构以及任何客户端框架&#xff0c;例…

HTML静态网页成品作业(HTML+CSS)——中华传统美德介绍网页(2个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;表格布局&#xff0c;未使用Javacsript代码&#xff0c;共有2个页面。…

sklearn 基础教程

scikit-learn&#xff08;简称sklearn&#xff09;是一个开源的机器学习库&#xff0c;它提供了简单和有效的数据分析和数据挖掘工具。sklearn是Python语言中最重要的机器学习库之一&#xff0c;广泛用于统计学习和数据分析。 以下是scikit-learn的基础教程&#xff0c;帮助您开…

Spring-kafka消费者消费的一些问题

前言 Spring Kafka 无缝集成了 Spring Boot、Spring Framework 及其生态系统中的其他项目&#xff0c;如 Spring Cloud。通过与 Spring Boot 的自动配置结合&#xff0c;开发者可以快速启动和配置 Kafka 相关的功能。无需编写大量样板代码即可实现 Kafka 的生产和消费功能&…

【面试干货】String、StringBuilder、StringBuffer 的区别

【面试干货】String、StringBuilder、StringBuffer 的区别 1、String2、StringBuffer3、StringBuilder4、性能对比5、使用建议 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;String、StringBuilder和StringBuffer是用…

云计算在保险行业的应用:太平财险团财险理赔新核心业务系统案例

随着科技的快速发展&#xff0c;云计算技术已经成为推动保险行业数字化转型的重要力量。云计算为保险公司提供了弹性、可扩展的计算资源&#xff0c;使其能够灵活应对业务高峰和低谷&#xff0c;提高业务运营效率和风控水平。太平财险与太平金科联合开发的“团财险理赔新核心业…