LinkedList详解

LinkedList详解

LinkedList是List接口的一个主要的实现类之一,基于链表的实现。以java8为例来了解一下LinkedList的源码实现

继承关系

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneablejava.io.Serializable
LinkedList
LinkedList

继承了AbstractSequentialList,实现了List接口、Deque接口、Cloneable接口、Serializable接口

关键变量

/**
* 链表的长度
*/

transient int size = 0;

/**
 * 头节点
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */

transient Node<E> first;

/**
 * 尾结点
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */

transient Node<E> last;

// 这里用到了一个内部类Node,看到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;
  }
}

构造器

// 无参默认构造器
public LinkedList() {
}

/**
 * 传入一个集合作为参数
 */

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

public boolean addAll(Collection<? extends E> c) {
  return addAll(size, c);
}

public boolean addAll(int index, Collection<? extends E> c) {
  // 检查是否越界
  checkPositionIndex(index);

  Object[] a = c.toArray();
  // 新增元素的数量
  int numNew = a.length;
  if (numNew == 0)
    return false;
 // 前置节点和后置节点
  Node<E> pred, succ;
  // index == size时表示是从链表尾部添加元素,所以前置节点为当前链表的尾结点
  if (index == size) {
    succ = null;
    pred = last;
  } else { // 不是从链表尾部添加,需要修改前置节点和后置节点
    succ = node(index);
    pred = succ.prev;
  }

  for (Object o : a) {
    @SuppressWarnings("unchecked") E e = (E) o;
    Node<E> newNode = new Node<>(pred, e, null);
    // 前置节点为null,表示这是头节点
    if (pred == null)
      first = newNode;
    else
      pred.next = newNode;
    pred = newNode;
  }
 // succ == null,说明是从尾部添加的,将最后一个节点赋给尾结点
  if (succ == null) {
    last = pred;
  } else {
    pred.next = succ;
    succ.prev = pred;
  }

  size += numNew;
  modCount++;
  return true;
}

// 获取index位置的节点
Node<E> node(int index) {
  // assert isElementIndex(index);
 // index小于链表长度的一半,从链表头部开始遍历
  if (index < (size >> 1)) {
    Node<E> x = first;
    for (int i = 0; i < index; i++)
      x = x.next;
    return x;
  } else { // index不小于链表长度的一半,从链表尾部开始遍历
    Node<E> x = last;
    for (int i = size - 1; i > index; i--)
      x = x.prev;
    return x;
  }
}

add方法

// 逻辑比较简单,与上边addAll类似,一通插
public void add(int index, E element) {
    checkPositionIndex(index);
  // index == size表示在链表尾部添加
    if (index == size)
        linkLast(element);
    else // 否则在中间插入  先找到index所在位置的节点
        linkBefore(element, node(index));
}

void linkLast(E e) {
  final Node<E> l = last;
  final Node<E> newNode = new Node<>(l, e, null);
  last = newNode;
  if (l == null)
   first = newNode;
  else
   l.next = newNode;
  size++;
  modCount++;
}

void linkBefore(E e, Node<E> succ) {
  // assert succ != null;
  final Node<E> pred = succ.prev;
  final Node<E> newNode = new Node<>(pred, e, succ);
  succ.prev = newNode;
  if (pred == null)
   first = newNode;
  else
   pred.next = newNode;
  size++;
  modCount++;
}

remove方法

LinkedList的remove方法比较

public E remove(int index) {
   // 检测该索引位置是否符合要求  index >= 0 && index < size
    checkElementIndex(index);
    return unlink(node(index));
}

// 找到当前索引位置的节点
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;
  }
}

unlink(Node<E> x) {
  // assert x != null;
  // 所要删除的元素
  final E element = x.item;
  // 所要删除的元素的后继节点
  final Node<E> next = x.next;
  // 所要删除的元素前驱结点
  final Node<E> prev = x.prev;
 // 前驱节点为null,表示为头节点,头节点指向后继节点即可
  if (prev == null) {
    first = next;
  } else {
    // 前驱节点的后继节点指向该元素的后继节点
    prev.next = next;
    x.prev = null;
  }
 // 后继节点为null,表示为尾节点,尾节点指向前驱节点即可
  if (next == null) {
    last = prev;
  } else {
    // 后继节点的前驱节点指向该元素的前驱节点
    next.prev = prev;
    x.next = null;
  }

  x.item = null;
  size--;
  modCount++;
  return element;
}

迭代器

LinkedList使用的迭代器是其父类AbstractSequentialList中的iterator方法,其迭代器是双向的,可以正向也可以反向

// AbstractSequentialList类方法
public Iterator<E> iterator() {
    return listIterator();
}

// LinkedList类方法
public ListIterator<E> listIterator(int index) {
  checkPositionIndex(index);
  return new ListItr(index);
}
//  与ArrayList的迭代器不同的是,ArrayList的迭代器实现的是Iterator接口 而LinkedList为ListIterator接口,相当于ArrayList的listIterator()方法
//  Iterator只能向前遍历,ListIterator可以向前也可以向后
private class ListItr implements ListIterator<E{
    private Node<E> lastReturned;
    private Node<E> next;
    private int nextIndex;
    private int expectedModCount = modCount;

    ListItr(int index) {
        // assert isPositionIndex(index);
        next = (index == size) ? null : node(index);
        nextIndex = index;
    }

    public boolean hasNext() {
        return nextIndex < size;
    }

    public E next() {
        checkForComodification();
        if (!hasNext())
            throw new NoSuchElementException();

        lastReturned = next;
        next = next.next;
        nextIndex++;
        return lastReturned.item;
    }

    public boolean hasPrevious() {
        return nextIndex > 0;
    }

    public E previous() {
        checkForComodification();
        if (!hasPrevious())
            throw new NoSuchElementException();

        lastReturned = next = (next == null) ? last : next.prev;
        nextIndex--;
        return lastReturned.item;
    }

    public int nextIndex() {
        return nextIndex;
    }

    public int previousIndex() {
        return nextIndex - 1;
    }

    public void remove() {
        checkForComodification();
        if (lastReturned == null)
            throw new IllegalStateException();

        Node<E> lastNext = lastReturned.next;
        unlink(lastReturned);
        if (next == lastReturned)
            next = lastNext;
        else
            nextIndex--;
        lastReturned = null;
        expectedModCount++;
    }

    public void set(E e) {
        if (lastReturned == null)
            throw new IllegalStateException();
        checkForComodification();
        lastReturned.item = e;
    }

    public void add(E e) {
        checkForComodification();
        lastReturned = null;
        if (next == null)
            linkLast(e);
        else
            linkBefore(e, next);
        nextIndex++;
        expectedModCount++;
    }

    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (modCount == expectedModCount && nextIndex < size) {
            action.accept(next.item);
            lastReturned = next;
            next = next.next;
            nextIndex++;
        }
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

https://zhhll.icu/2021/java基础/集合/3.LinkedList详解/

本文由 mdnice 多平台发布

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

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

相关文章

第十五届蓝桥杯模拟赛B组(第二期)C++

前言&#xff1a; 第一次做蓝桥模拟赛的博客记录&#xff0c;可能有很多不足的地方&#xff0c;现在将第十五届蓝桥杯模拟赛B组&#xff08;第二期&#xff09;的题目与代码与大家进行分享&#xff0c;我是用C做的&#xff0c;有好几道算法题当时自己做的也是一脸懵&#xff0c…

DELL EMC unity 存储系统日志收集方法

对于一些非简单的硬件故障&#xff0c;解决故障最有效、最快速的方法就是收集日志&#xff0c;而不是瞎搞。常见的乱搞方法就是 1. reimage系统‘ 2. 更换控制器&#xff1b;3&#xff0c; 重启。 本文详细介绍了图形界面GUI和命令行CLI下如何收集DELL EMC Unity日志的方法和常…

WPS导出的PDF比较糊,和原始的不太一样,将带有SVG的文档输出为PDF

一、在WPS的PPT中 你直接输出PDF可能会导致一些问题&#xff08;比如照片比原来糊&#xff09;/ 或者你复制PPT中的图片到AI中类似的操作&#xff0c;得到的照片比原来糊&#xff0c;所以应该选择打印-->高级打印 然后再另存为PDF 最后再使用AI打开PDF文件再复制到你想用…

挑选数据可视化工具:图表类型、交互功能与数据安全

作为一名数据分析师&#xff0c;我经常需要使用各种数据可视化工具来将数据以直观、清晰的方式呈现出来&#xff0c;以便更好地理解和分析。在市面上的众多可视化工具中&#xff0c;我根据实际需求和项目特点进行选择。本文将从以下几个角度对市面上的数据可视化工具进行对比&a…

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级 在之前的开发过程中&#xff0c;需要实现卡片轮播效果&#xff0c;但是卡片轮播需要中间大、两边小一些的效果&#xff0c;这里就使用到了Swiper。具体效果如视频所示 添加链接描述 这里需要的效果是中间大、两边…

安装postgresql驱动及python使用pyodbc指定postgresql驱动调用postgresql

注&#xff1a;Python解释器版本(32位/64位)和postgresql驱动版本(32位/64位)需一致。 一、安装postgresql驱动 https://www.postgresql.org/ftp/odbc/versions/msi/ &#xff08;1&#xff09;32位&#xff1a; &#xff08;2&#xff09;64位&#xff1a; 双击安装。全程默…

高精度加法,减法,乘法,除法(上)(C语言)

前言 加&#xff0c;减&#xff0c;乘&#xff0c;除这些运算我们自然信手捏来&#xff0c;就拿加法来说&#xff0c;我们要用c语言编程算ab的和&#xff0c;只需让sum ab即可&#xff0c;可是这是局限的&#xff0c;我们都知道int的表示的最大值为2147483647&#xff08;32位…

2023.12.3 分布式SQL查询引擎-Presto

目录 目录 1.Prosto简介 Apache Hadoop-MapReduce Apache Hive 2.Presto的优缺点 3.个人自用启动服务 个人自用启动服务 3.Presto的架构 4.presto和hive的区别 5.presto优化 6.Presto-内存调优 1.Prosto简介 Apache Hadoop-MapReduce 优点&#xff1a;统一、通用、简…

C - 语言->内存函数

目录 系列文章目录 前言 1. memcpy使⽤和模拟实现 1.2 memcpy函数的模拟实现: 2. memmove 使⽤和模拟实现 2.1memmove的模拟实现&#xff1a; 3. memset 函数的使⽤ 4. memcmp 函数的使⽤ 系列文章目录 ✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff…

论文精读 MOG 埃里克·格里姆森

Adaptive background mixture models for real-time tracking 用于实时跟踪的自适应背景混合模型 1999年的MOG&#xff0c;作者是麻省理工学院前校长——埃里克格里姆森&#xff08;W. Eric L. Grimson&#xff09;。 目录 摘要 结论 1 介绍 1.1 以往的工作和现在的缺点 …

学生成绩管理系统(Java)

开发环境: Windows 11 IDEA 2021.3.3 需求: package com.it.neu;import java.util.ArrayList; import java.util.Scanner;import static java.time.Clock.system;class Student { //创建学生类private String Stu_name;private String Stu_id;public Student(String id, S…

履带吊,笔记

0.前言 履带吊使用了与传统的门桥式起重机不同的技术路线。因为它是移动式设备&#xff0c;所以它的动力是燃油发动机。为了精确调控升降。它的整套动力系统似乎采用了某种液压传动系统。履带吊国内也有生产商。但是下文中&#xff0c;还是从国外的一款产品说起。这款产品的pd…

Android蓝牙协议栈fluoride(二) - 软件框架

概述 fluoride 协议栈在整个软件框架中作为一个中间件的角色&#xff0c;向上对接APP&#xff0c;向下对接蓝牙芯片。fluoride采用C语言实现&#xff0c;与APP(Jave)通信采用JNI机制&#xff1b;与蓝牙芯片通信使用HCI硬件接口&#xff08;HCI软件协议参考蓝牙核心规范&#x…

Linux环境搭建(Ubuntu22.04)+ 配置共享文件夹(Samba)

Linux开发环境准备 搭建Linux开发环境所需要的软件如下&#xff1a; VMware虚拟机&#xff1a;用于运行Linux操作系统的虚拟机软件之一&#xff0c;VMware下载安装在文章中不做说明&#xff0c;可自行百度谢谢Ubuntu光盘镜像&#xff1a;用于源代码编译&#xff0c;有闲置计算…

Docker部署开源分布式任务调度系统DolphinScheduler与远程访问办公

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

Python语言基础知识(一)

文章目录 1、Python内置对象介绍2、标识符与变量3、数据类型—数字4、数据类型—字符串与字节串5、数据类型—列表、元组、字典、集合6、运算符和表达式7、运算符和表达式—算术运算符8、运算符和表达式—关系运算符9.1、运算符和表达式— 成员测试运算符in9.2、运算符和表达式…

C++中单引号‘‘和双引号““的区别

操作系统&#xff1a;Windows 10 IDE&#xff1a;CLion 单引号&#xff1a;表示一个字符&#xff0c;例如 a 双引号""&#xff1a;表示一个字符串&#xff0c;例如 "a" 在C中&#xff0c;使用双引号可以方便地创建字符串&#xff0c;而使用单引号可以方便…

使用PCSS实现的实时阴影效果

PCSS的技术可以使得阴影呈现出近硬远软的效果&#xff0c;并且能够实时实现。 其核心理念是通过模拟光源的面积来产生更自然、更柔和的阴影边缘。 具体步骤&#xff1a; 1、生成shadowmap 2、在进行阴影的比较时候进行平均&#xff0c;并非之前的shadow map 或者之后完全的阴影…