目录

1. 是什么

底层由双向链表实现的顺序表

有序、可以重复

2. 如何使用

public class LinkedListTest
{
public static void main(String[] args)
{
LinkedList<String> list = new LinkedList<>();
list.add("1");
list.add("2");
list.addFirst("3");
list.addLast("4");
System.out.println(list);
list.remove(0);
list.remove("2");
list.remove();
list.removeFirst();
list.removeLast(); }
}

3. 原理分析

3.1. uml



可以看出LinkedList是个List、双端队列、可序列化、可克隆

3.2. 构造方法

由头节点、尾节点、长度构成

public class LinkedList<E>
extends AbstractSequentialList<E>//提供了List的骨架实现
implements List<E>/*List接口*/, Deque<E>/*双端队列*/, Cloneable, java.io.Serializable
{ //属性
transient int size = 0;//长度
transient Node<E> first;//头节点
transient Node<E> last;//尾节点 //构造方法
public LinkedList() {
}
}

3.2.1. 队列的节点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;
}
}

结构如下图:

3.3. add方法

  • O(1)
public boolean add(E e) {
//调用linkLast方法
linkLast(e);
return true;
}

3.3.1. 插入到链表尾部

  • linkLast
void linkLast(E e) {
//保存尾节点
final Node<E> l = last;
//构造新的节点,prev指向last节点
final Node<E> newNode = new Node<>(l, e, null);
//把尾节点更新为新的节点
last = newNode;
//如果原来的尾节点为空,那么是第一个节点。此时更新first为新节点
if (l == null)
first = newNode;
else
//否则更新尾节点的next为新的节点
l.next = newNode;
size++;
modCount++;
}

3.3.2. 构造新节点【prev指向尾节点,next为null】

//保存尾节点
final Node<E> l = last;
//构造新的节点,prev指向last节点
final Node<E> newNode = new Node<>(l, e, null);

3.3.3. 更新尾节点

//如果原来的尾节点为空,那么是第一个节点。此时更新first为新节点
if (l == null)
first = newNode;
else
//否则更新尾节点的next为新的节点
l.next = newNode;

3.3.4. 更新size

size++;

3.4. addLast方法

  • O(1)
public void addLast(E e) {
//调用linkLast
linkLast(e);
}
  • linkLast

    参考add方法

3.5. addFirst方法

  • O(1)
public void addFirst(E e) {
//简单的调用linkFirst
linkFirst(e);
}

3.5.1. 头部插入节点

  • linkFirst

private void linkFirst(E e) {
//先保存原头节点
final Node<E> f = first;
//新建节点,next指向原头节点
final Node<E> newNode = new Node<>(null, e, f);
//更新头节点为新的节点
first = newNode;
//如果原头节点为空,那么是第一个元素,更新last节点为新的节点
if (f == null)
last = newNode;
else
//否则更新原头节点的prev为新的节点
f.prev = newNode; size++;
modCount++;
}

3.5.2. 构造新节点【prev指向null,next指向头节点】

//先保存原头节点
final Node<E> f = first;
//新建节点,next指向原头节点
final Node<E> newNode = new Node<>(null, e, f);

3.5.3. 更新头节点

//如果原头节点为空,那么是第一个元素,更新last节点为新的节点
if (f == null)
last = newNode;
else
//否则更新原头节点的prev为新的节点
f.prev = newNode;

3.5.4. 更新size

size++;

3.6. remove方法【根据下标删除】

  • O(N)

  • 修改这个元素的前后元素的next和prev指针

public E remove(int index) {
//检查是否越界
checkElementIndex(index);
//根据下标找到相应的节点,并把这个节点删除
return unlink(node(index));
}

3.6.1. 遍历找到这个位置的元素

  • node

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;
}
}

3.6.2. 更新该节点前后节点的指针

E unlink(Node<E> x) {
// assert x != null;
//首先保存当前节点的prev,next,和item值
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev; //更新prev
if (prev == null) {
//前一个节点为空(说明当前节点是头节点),那么把first指向当前节点的下一个节点即可
first = next;
} else {
//前一个节点的next指向当前节点的下一个节点
prev.next = next;
//help gc
x.prev = null;
} //更新next
if (next == null) {
//后一个节点为空(说明当前节点是尾节点),那么把last指向当前节点的上一个节点即可
last = prev;
} else {
//后一个节点的prev指向当前节点的上一个节点
next.prev = prev;
//help gc
x.next = null;
} //help gc
x.item = null;
size--;//更新size
modCount++;
return element;
}

3.7. remove方法【按照元素删除】

  • O(N)
public boolean remove(Object o) {
//==null
if (o == null) {
//遍历找到node
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
//equals
} else {
//遍历找到node
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}

3.7.1. 遍历找到这个元素

for (Node<E> x = first; x != null; x = x.next)
{
//...
}

3.7.2. 更新该节点前后节点的指针

3.8. remove方法【无参】

  • O(1)
public E remove() {
//简单调用removeFirst
return removeFirst();
}

3.8.1. 删除头部节点

  • removeFirst

public E removeFirst() {
//头节点
final Node<E> f = first;
if (f == null)
//为null直接抛出异常
throw new NoSuchElementException();
//调用unlinkFirst从链表删除头节点
return unlinkFirst(f);
}

3.8.2. 把原来头节点的next节点更新为头节点

  • unlinkFirst

private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
//保存item,next
final E element = f.item;
final Node<E> next = f.next; // help GC
f.item = null;
f.next = null; //first直接指向头节点的next
first = next;
//如果头节点的next为空,说明链表中只有一个节点
if (next == null)
//更新last指向null
last = null;
else
//否则更新头节点的next节点的prev指针
next.prev = null;
size--;
modCount++;
return element;
}

3.9. removeLast方法

  • O(1)
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}

3.9.1. 把原来尾节点的prev节点更新为尾节点

  • unlinkLast
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
//保存尾节点的item值和prev
final E element = l.item;
final Node<E> prev = l.prev;
// help GC
l.item = null;
l.prev = null;
//更新last为原尾节点的prev
last = prev;
//只有一个元素
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}

3.Java SDK源码分析系列笔记-LinkedList的更多相关文章

  1. MyCat源码分析系列之——结果合并

    更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...

  2. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  3. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  4. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  5. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

  6. spring源码分析系列 (5) spring BeanFactoryPostProcessor拓展类PropertyPlaceholderConfigurer、PropertySourcesPlaceholderConfigurer解析

    更多文章点击--spring源码分析系列 主要分析内容: 1.拓展类简述: 拓展类使用demo和自定义替换符号 2.继承图UML解析和源码分析 (源码基于spring 5.1.3.RELEASE分析) ...

  7. spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.BeanFactoryPostProcessor.BeanDefinitionRegistryPostProcessor简述与demo示例 ...

  8. spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.InstantiationAwareBeanPostProcessor简述与demo示例 二.InstantiationAwareBean ...

  9. spring源码分析系列 (2) spring拓展接口BeanPostProcessor

    Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...

  10. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

随机推荐

  1. 使用 PHP cURL 实现 HTTP 请求类

    类结构 创建一个 HttpRequest 类,其中包括初始化 cURL 的方法.不同类型的 HTTP 请求方法,以及一些用于处理响应头和解析响应内容的辅助方法. 初始化 cURL 首先,创建一个私有方 ...

  2. CDS是一个企业级的持续交付和DevOps自动化开源平台

    CDS是一个企业级的持续交付和DevOps自动化开源平台 弹性 CDS资源/worker是按需启动的,以确保用户的等待时间较短,并且不会过度消耗空闲资源 可扩展的 在CDS中,任何类型的操作(Kube ...

  3. 一文速通Python并行计算:06 Python多线程编程-基于队列进行通信

    一文速通 Python 并行计算:06 Python 多线程编程-基于队列进行通信 摘要: 队列是一种线性数据结构,支持先进先出(FIFO)操作,常用于解耦生产者和消费者.慢速生产-快速消费场景中,队 ...

  4. 从DeepSeek看算法备案&大模型备案

    一.deepseek的备案情况 (一)算法备案情况 在算法备案系统网站上,北京深度求索人工智能基础技术研究有限公司和杭州深度求索人工智能基础技术研究有限公司分别进行了两个算法备案.从公司名称来看,正如 ...

  5. springmvc实现转发和重定向

    一. @RequestMapping("/testVoid") public String testVoid(HttpServletRequest request){ //转发方式 ...

  6. 前端ai工具v0使用配置

    资料 ai工具Vo Installation - Tailwind CSS 以vue3 + sass为例,配置如下 安装tailwindcss npm install -D tailwindcss n ...

  7. Traefik,想说爱你不容易:一场动态反向代理的心累之旅

    前言:技术选型的初心 在微服务盛行.容器部署逐渐常态化的今天,"动态反向代理"显得尤为重要. Traefik 凭借其原生支持 Docker.自动生成路由.集成 Let's Encr ...

  8. MySQL 中 TEXT 类型最大可以存储多长的文本?

    在MySQL中,TEXT类型用于存储较长的文本数据.TEXT类型的最大存储长度取决于表的字符集和存储引擎.具体来说,TEXT类型的最大存储长度为: TEXT:最大存储 65,535 字节(约 64 K ...

  9. 图解-六种常见开源协议的比较(GPL,Mozilla,LGPL,BSD,Apache,MIT)

    附: 五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)

  10. [随记]-SpringMVC中的handler到底是什么东西

    HandlerMapping 初始化时候的 HandlerMapping 有,按顺序排列: requestMappingHandlerMapping beanNameHandlerMapping -& ...