关于LinkedList

本文详细介绍了LinkedList作为List接口实现类的内部结构和操作,包括其双端链表特性、节点结构、构造方法以及常用方法的使用示例。内容涵盖了LinkedList的线程安全性、增删查改效率以及作为堆栈、队列和双端队列的特性。此外,还通过示例代码展示了LinkedList的各种操作,如添加、删除、访问元素以及Queue和Deque接口的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

之前的内容中,我们分析总结了List集合及实现类ArrayList源码,并对数组扩容原理进行深度解析。本节内容主要学习总结接口List的另一个重要实现类LinkedList。先看一下LinkedList的UML类图:

  • LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
  • LinkedList 实现 List 接口,能对它进行队列操作。

LinkedList是一个实现了List接口Deque接口双端链表

LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性。

LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:

List<Object> linkedList = Collections.synchronizedList(new LinkedList<>());

LinkedList类的内部结构分析

LinkedList内部结构

来看一下LinkedList类的内部私有类Node:

    private static class Node<E> {
        // 节点
        E item;
        // 后继节点
        Node<E> next;
        // 前驱节点
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

 这个类就代表双端链表的节点Node,有三个属性:前驱节点、本节点值、后继节点。


LinedList类Demo

【1】无参构造方法

/**
 * Constructs an empty list.
 */
 public LinkedList() {
 
 }

【2】按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。

public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

举个栗子:

package com.hl.magic.items.day11.collection.list;

import org.junit.Before;
import org.junit.Test;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * 链表LinkedList
 * List接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。
 * 除了实现 List 接口外,LinkedList类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。
 * 这些操作允许将链接列表用作堆栈、队列或双端队列。
 * <p>
 * 链表LinkedList实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
 * <p>
 * <p>
 * 【1】LinkedList特点
 * 线程不同步,线程不安全
 * 增删的速度快,查询的速度慢
 * <p>
 * 【3】LinkedList集合存储的数据结构是链表结构,提供了大量的首尾操作的方法。
 * void addFirst(E e);
 * void addLast(E e);
 * 
 * E  	getFirst();
 * E	getLast();
 * 
 * E	removeFirst();
 * E	removeLast();
 *
 * @author administrator
 * @date 2020/09/25 15:13
 */
public class LinkedListBaseTest {

	private LinkedList<Integer> linkedList;

    /**
     * LinkedList的基本操作
     */
	@Before
	public void test_before() {
		linkedList = new LinkedList<>();
        // 添加元素到链表开头
        linkedList.addFirst(0);
        // 添加元素到链表开头
        linkedList.add(1);
        // 将元素添加在指定索引位置处
        linkedList.add(2, 2);
        // 添加元素到链表末尾
        linkedList.addLast(3);
        System.out.println("-----------------------------------------");
        System.out.println("LinkedList(直接输出的): " + linkedList);
	}

	@Test
	public void test_LinkedList() {
        System.out.println("-----------------------------------------");
        // 返回此列表的第一个元素
        System.out.println("getFirst()获得第一个元素: " + linkedList.getFirst());
        // 返回此列表的最后一个元素
        System.out.println("getLast()获得第最后一个元素: " + linkedList.getLast());
        // 移除并返回此列表的第一个元素
        System.out.println("removeFirst()删除第一个元素并返回该元素: " + linkedList.removeFirst());
        // 移除并返回此列表的最后一个元素
        System.out.println("removeLast()删除最后一个元素并返回该元素: " + linkedList.removeLast());
        System.out.println("After remove:" + linkedList);
        // 判断此列表包含指定元素,如果是,则返回true
        System.out.println("contains()方法判断列表是否包含1这个元素:" + linkedList.contains(1));
        // 返回此列表的元素个数
        System.out.println("该linkedList的大小 : " + linkedList.size());
        System.out.println("-----------------------------------------");
	}

    /**
     * 位置访问操作
     */
    @Test
    public void test_set() {
        System.out.println("-----------------------------------------");
        // 将此列表中指定位置的元素替换为指定的元素
        linkedList.set(1, 3);
        System.out.println("After set(1, 3):" + linkedList);
        // 返回此列表中指定位置处的元素
        System.out.println("get(1)获得指定位置(这里为1)的元素: " + linkedList.get(1));
        System.out.println("-----------------------------------------");
    }

    /**
     * search位置访问操作
     */
    @Test
    public void test_search(){
        System.out.println("-----------------------------------------");
        linkedList.add(3);
        System.out.println("After add(3):" + linkedList);
        // 返回此列表中首次出现的指定元素的索引
        System.out.println("indexOf(3): " + linkedList.indexOf(3));
        // 返回此列表中最后出现的指定元素的索引
        System.out.println("lastIndexOf(3): " + linkedList.lastIndexOf(3));
        System.out.println("-----------------------------------------");
    }
    /**
     * Queue操作
     */
    @Test
    public void test_queue(){
        System.out.println("-----------------------------------------");
        // 获取但不移除此列表的头
        System.out.println("peek(): " + linkedList.peek());
        // 获取但不移除此列表的头
        System.out.println("element(): " + linkedList.element());

        // 获取并移除此列表的头
        Integer poll = linkedList.poll();
        System.out.println("poll element: " + poll);
        System.out.println("After poll():" + linkedList);

        // 获取并移除此列表的头
        linkedList.remove();
        System.out.println("After remove():" + linkedList);

        // 将指定元素添加到此列表的末尾
        linkedList.offer(4);
        System.out.println("After offer(4):" + linkedList);
        System.out.println("-----------------------------------------");
    }
    /**
     * Deque操作
     */
    @Test
    public void test_deque() {
        System.out.println("-----------------------------------------");
        // 在此列表的开头插入指定的元素
        linkedList.offerFirst(2);
        System.out.println("After offerFirst(2):" + linkedList);
        // 在此列表末尾插入指定的元素
        linkedList.offerLast(5);
        System.out.println("After offerLast(5):" + linkedList);
        // 获取但不移除此列表的第一个元素
        System.out.println("peekFirst(): " + linkedList.peekFirst());
        // 获取但不移除此列表的第一个元素
        System.out.println("peekLast(): " + linkedList.peekLast());
        // 获取并移除此列表的第一个元素
        linkedList.pollFirst();
        System.out.println("After pollFirst():" + linkedList);
        // 获取并移除此列表的最后一个元素
        linkedList.pollLast();
        System.out.println("After pollLast():" + linkedList);
        // 将元素推入此列表所表示的堆栈(插入到列表的头)
        linkedList.push(2);
        System.out.println("After push(2):" + linkedList);
        // 从此列表所表示的堆栈处弹出一个元素(获取并移除列表第一个元素)
        linkedList.pop();
        System.out.println("After pop():" + linkedList);
        linkedList.add(3);
        // 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表)
        linkedList.removeFirstOccurrence(3);
        System.out.println("After removeFirstOccurrence(3):" + linkedList);
        // 从此列表中移除最后一次出现的指定元素(从尾部到头部遍历列表)
        linkedList.removeLastOccurrence(3);
        System.out.println("After removeFirstOccurrence(3):" + linkedList);
        System.out.println("-----------------------------------------");
    }

    @Test
    public void test_for(){
        System.out.println("-----------------------------------------");
        linkedList.clear();
        for (int i = 0; i < 100000; i++) {
            linkedList.add(i);
        }
        // iterator迭代器遍历
        long start = System.currentTimeMillis();
        Iterator<Integer> iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            iterator.next();
        }
        long end = System.currentTimeMillis();
        System.out.println("Iterator:" + (end - start) + " ms");

        // 顺序遍历(随机遍历)
        start = System.currentTimeMillis();
        for (int i = 0; i < linkedList.size(); i++) {
            linkedList.get(i);
        }
        end = System.currentTimeMillis();
        System.out.println("for:" + (end - start) + " ms");

        // 另一种增强for循环遍历
        start = System.currentTimeMillis();
        for (Integer i : linkedList)
            ;
        end = System.currentTimeMillis();
        System.out.println("foreach:" + (end - start) + " ms");

        // 通过pollFirst()或pollLast()来遍历LinkedList
        LinkedList<Integer> temp1 = new LinkedList<>();
        temp1.addAll(linkedList);
        start = System.currentTimeMillis();
        while (temp1.size() != 0) {
            temp1.pollFirst();
        }
        end = System.currentTimeMillis();
        System.out.println("pollFirst()或pollLast():" + (end - start) + " ms");

        // 通过removeFirst()或removeLast()来遍历LinkedList
        LinkedList<Integer> temp2 = new LinkedList<>();
        temp2.addAll(linkedList);
        start = System.currentTimeMillis();
        while (temp2.size() != 0) {
            temp2.removeFirst();
        }
        end = System.currentTimeMillis();
        System.out.println("removeFirst()或removeLast():" + (end - start) + " ms");
        System.out.println("-----------------------------------------");
    }
}
(请详细解析相关概念和描述,联网解析,中文解析)看看关于LinkedList源码开头的注释 • Doubly-linked list implementation of the {@code List} and {@code Deque} • interfaces. Implements all optional list operations, and permits all • elements (including {@code null}). * • <p>All of the operations perform as could be expected for a doubly-linked • list. Operations that index into the list will traverse the list from • the beginning or the end, whichever is closer to the specified index. * • <p><strong>Note that this implementation is not synchronized.</strong> • If multiple threads access a linked list concurrently, and at least • one of the threads modifies the list structurally, it <i>must</i> be • synchronized externally. (A structural modification is any operation • that adds or deletes one or more elements; merely setting the value of • an element is not a structural modification.) This is typically • accomplished by synchronizing on some object that naturally • encapsulates the list. 从这段注释中,我们可以得知 LinkedList 是通过一个双向链表来实现的,它允许插入所有元素,包括 null,同时,它是线程不同步的。 LinkedList集合底层结构是带头尾指针的双向链表LinkedList是非线程安全的。 LinkedList集合中存储元素的特点:有序可重复,元素带有下标,从0开始,以1递增。 LinkedList集合的优点:在指定位置插入/删除元素的效率较高;缺点:查找元素的效率不如ArrayList。
04-04
<think>好的,我现在需要帮助用户解析Java LinkedList的源码注释,特别是关于双向链表线程安全性和插入删除效率的部分。用户提到了几个引用,我需要结合这些引用来组织回答。 首先,用户提供的引用中有关于LinkedList的基本结构,比如双向链表,继承自AbstractSequentialList,以及线程不安全的问题。引用[3]提到LinkedList是双向链表,可以作为堆栈、队列等使用,这应该作为开头部分的结构描述。 接下来是线程安全性。引用[2]和[4]都说明了LinkedList是线程不安全的,需要外部同步,或者使用Collections.synchronizedList。引用[4]还详细解释了modCount和ConcurrentModificationException的产生机制,这部分需要详细说明,尤其是迭代器遍历时多线程修改的问题。 然后是插入和删除效率。引用[1]指出在指定位置插入时LinkedList比ArrayList高效,因为不需要数组复制。但可能用户需要更详细的解释,比如节点操作的时间复杂度和具体方法,比如add和remove。引用[5]中的remove方法解析,通过遍历找到元素后调用unlink,说明删除操作的时间复杂度是O(n),但unlink本身是O(1)。需要区分按索引和按元素删除的不同情况。 源码注释中的关键点可能需要提取,比如线程不安全、双向链表结构、允许null值等。用户还希望中文解析,所以需要用简洁的中文解释这些概念。 此外,用户的问题还包括生成相关问题,需要根据内容提出相关问题,比如结构特点、线程安全保证、与ArrayList的比较、应用场景等。 需要确保回答结构清晰,分点说明,引用正确的来源,并在末尾添加相关问题。同时注意使用中文,避免使用Markdown格式,但根据用户系统指令,可能允许适当的分段和代码块。 最后检查是否覆盖了用户的所有需求:源码注释中的概念、双向链表线程安全、插入删除效率,以及引用相关的内容。确保每个部分都有对应的引用支持,例如线程安全部分引用[2][4],插入删除效率引用[1][5],结构部分引用[3]。</think>### Java LinkedList 源码注释解析 #### 1. **双向链表结构** LinkedList 是基于双向链表实现的集合类,每个节点(`Node`)包含前驱指针(`prev`)、后继指针(`next`)和存储的数据(`item`)。这种结构使得插入和删除操作的时间复杂度为 $O(1)$(仅操作指针),但查找元素需要 $O(n)$ 时间(需遍历链表)[^3]。 源码注释中明确说明: > `LinkedList` 实现了 `List` 和 `Deque` 接口,可作为队列、堆栈或双端队列使用。 #### 2. **线程安全性** LinkedList **非线程安全**,多线程环境下需通过外部同步控制(如 `Collections.synchronizedList`)保证安全[^2][^4]。源码注释指出: > 若多个线程并发修改链表,需通过外部同步机制保证一致性。迭代器遍历时若其他线程修改链表结构,会触发 `ConcurrentModificationException`。 具体机制: - **`modCount` 与 `expectedModCount`**: 迭代器初始化时会记录 `modCount`(链表结构修改计数器)。遍历过程中若发现 `modCount != expectedModCount`,说明链表被外部修改,立即抛出异常。 #### 3. **插入与删除效率** - **插入效率**: 在指定索引位置插入元素时,LinkedList 只需调整相邻节点的指针($O(1)$),而 ArrayList 需要复制数组($O(n)$)。因此,频繁插入场景下 LinkedList 更高效[^1]。 源码中 `add(int index, E element)` 方法通过 `node(index)` 找到目标位置节点后直接插入新节点。 - **删除效率**: `remove(Object o)` 方法需遍历链表找到目标元素($O(n)$),但实际删除操作(`unlink(Node<E> x)`)仅需修改指针($O(1)$)[^5]。例如删除首个匹配的 `null` 值: ```java for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); // 断开前后指针,更新链表头尾 return true; } } ``` #### 4. **其他关键注释说明** - **允许 `null` 值**:LinkedList 允许存储 `null`,在 `remove` 和 `contains` 方法中需特殊处理。 - **继承关系**:继承自 `AbstractSequentialList`,实现了链式存储的抽象逻辑[^3]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洒家肉山大魔王

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值