1、简介

LinkedList 底层使用的是 双向链表的数据结构

2、类图(JDK 1.8)

下图是LinkedList实现的接口和继承的类关系图:

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

1、实现了四个接口

1.1 java.util.List 接口,提供数组的添加、删除、修改、迭代遍历等操作。

1.2 java.io.Serializable 接口,表示 LinkedList 支持序列化的功能。

1.3 java.lang.Cloneable 接口,表示 LinkedList 支持克隆。

1.4 java.util.Deque接口,是一种双向线性集合,可以再两端对集合进行插入或者删除操作,所以既可以当队列(FIFO),也可以当栈(LIFO),Deque继承java.util.Queue。

2、继承了一个类

2.1 java.util.AbstractSequentialList抽象类,它继承了AbstractList抽象类,并且有

public abstract ListIterator<E> listIterator(int index);抽象方法,返回一个由LinkedList实现的ListIterator迭代器,用来支持AbstractList抽象类的get(int index),set(int index, E element),add(int index, E element)remove(int index) 等一些支持随机访问的方法。

3、常用方法

添加元素 add(E e)

    //链表添加元素
public boolean add(E e) {
linkLast(e);//往链表尾部添加元素
return true;
}
void linkLast(E e) {
final Node<E> l = last;//临时变量l=last,后面会直接修改last节点 指向 新加入的节点
final Node<E> newNode = new Node<>(l, e, null);//新元素,创建成一个节点
last = newNode;//最后节点,指向新加入的节点
if (l == null)//判断last是否为空,为空代表是第一次新增节点
first = newNode;//首节点指向新加入的节点
else//last不为空表示当前链表有值,让l节点的参数 next指向新加入的节点
l.next = newNode;
size++;//链表大小+1
modCount++;//链表修改次数+1
} 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;
}
}

链表指定位置添加元素 add(int index, E element)

    public void add(int index, E element) {
checkPositionIndex(index);//判断index是否>=0,并且index<=size,否则抛出IndexOutOfBoundsException异常 if (index == size)//判断index等于size直接添加到链表尾部
linkLast(element);//同add(E e)方法
else
linkBefore(element, node(index));//node()方法先根据index标在链表取得对应到Node节点,并把新元素,添加到Node节点之前
} //根据下标获得节点
Node<E> node(int index) {
// assert isElementIndex(index); //size >> 1 等同 size除以2
//判断index 下标是在链表的前半部分,是则从链表头部开始遍历,否则从链表尾部向前遍历
//加了一个判断,最多只需要遍历一半链表即可,复杂度从O(n)降到了O(1)
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;
}
}

根据下标删除节点 remove(int index)

   //根据下标删除节点
public E remove(int index) {
checkElementIndex(index);//判断下标是否越界
return unlink(node(index));
} //链表-删除节点
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;//删除节点的元素
final Node<E> next = x.next;//删除节点的下一个节点
final Node<E> prev = x.prev;//删除节点的上一个节点 if (prev == null) {//prev等于null,说明是第一个节点
first = next;
} else {
prev.next = next;//删除节点的上一个节点,next参数指向 删除节点的下一个节点
x.prev = null;//删除节点的上一个节点,设置为null取消关联
} if (next == null) {//next等于null,说明是尾部节点
last = prev;
} else {
next.prev = prev;//删除节点的下一个节点,参数prev指向 删除节点的上一个节点
x.next = null;//删除节点的下一个节点,设置为null取消关联
} x.item = null;//节点元素设置为null,方便gc回收
size--;//链表大小-1
modCount++;//链表修改次数+1
return element;//返回删除的元素值
}

根据元素删除节点 remove(Object o)

    //删除链表中第一个匹配o的元素
public boolean remove(Object o) {
if (o == null) {//先判断o是否为空
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {//判断元素是否为null
unlink(x);//链表删除元素
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {//判断元素是否相等
unlink(x);//链表删除元素
return true;
}
}
}
return false;
}

根据下标获得元素 get(int index)

    //根据下标获得节点
public E get(int index) {
checkElementIndex(index);//判断index是大于0,并且小于等于size,否则抛出异常
return node(index).item;//node()方法获取下标对应的Node,并返回Node的item
}

时间复杂度

添加元素 add(E e)

最好时间复杂度是 O(1) ,最坏时间复杂度是 O(n) ,平均时间复杂度是 O(n) 。

最好时间复杂度发生在头部、或尾部添加的情况。

链表指定位置添加元素 add(int index, E element)

最好时间复杂度是 O(1) ,最坏时间复杂度是 O(n) ,平均时间复杂度是 O(n) 。

最好时间复杂度发生在头部、或尾部添加的情况。

根据下标删除节点 remove(int index)

最好时间复杂度是 O(1) ,最坏时间复杂度是 O(n) ,平均时间复杂度是 O(n) 。

最好时间复杂度发生在头部、或尾部移除的情况。

根据元素删除节点 remove(Object o)

最好时间复杂度是 O(1) ,最坏时间复杂度是 O(n) ,平均时间复杂度是 O(n) 。

最好时间复杂度发生在头部移除的情况。

根据下标获得元素 get(int index)

最好时间复杂度是 O(1),平均时间复杂度是 O(n) ,查找指定元素的平均时间复杂度是 O(n) 。

最好时间复杂度发生在下标为0或者等于size的位置,因为LinkedList会根据index是否超过了 size除以2 来作为依据看是从链表头部往后面遍历,还是链表尾部向前遍历,所以基本上只需要遍历一半的链表就能找到对应下标的元素了。

小结

1、LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Node的引用地址即可

2、适用于频繁的元素修改的场景,对于查询场景比较多的适合请适用ArrayList这种数据结构

java集合-链表LinkedList的更多相关文章

  1. 【Java集合】LinkedList详解前篇

    [Java集合]LinkedList详解前篇 一.背景 最近在看一本<Redis深度历险>的书籍,书中第二节讲了Redis的5种数据结构,其中看到redis的list结构时,作者提到red ...

  2. Java集合:LinkedList源码解析

    Java集合---LinkedList源码解析   一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据re ...

  3. 死磕 java集合之LinkedList源码分析

    问题 (1)LinkedList只是一个List吗? (2)LinkedList还有其它什么特性吗? (3)LinkedList为啥经常拿出来跟ArrayList比较? (4)我为什么把LinkedL ...

  4. java集合之linkedList链表基础

    LinkedList链表: List接口的链接列表实现.允许存储所有元素(包含null).使用频繁增删元素. linkedList方法: void addFirst(E e) 指定元素插入列表的开头 ...

  5. Java 集合之LinkedList源码分析

    1.介绍 链表是数据结构中一种很重要的数据结构,一个链表含有一个或者多个节点,每个节点处理保存自己的信息之外还需要保存上一个节点以及下一个节点的指针信息.通过链表的表头就可以访问整个链表的信息.Jav ...

  6. Java集合干货——LinkedList源码分析

    前言 在上篇文章中我们对ArrayList对了详细的分析,今天我们来说一说LinkedList.他们之间有什么区别呢?最大的区别就是底层数据结构的实现不一样,ArrayList是数组实现的(具体看上一 ...

  7. Java集合(五)--LinkedList源码解读

    首先看一下LinkedList基本源码,基于jdk1.8 public class LinkedList<E> extends AbstractSequentialList<E> ...

  8. Java集合:LinkedList (JDK1.8 源码解读)

    LinkedList介绍 还是和ArrayList同样的套路,顾名思义,linked,那必然是基于链表实现的,链表是一种线性的储存结构,将储存的数据存放在一个存储单元里面,并且这个存储单元里面还维护了 ...

  9. Java集合之LinkedList

    一.LinkedList概述 1.初识LinkedList 上一篇中讲解了ArrayList,本篇文章讲解一下LinkedList的实现. LinkedList是基于链表实现的,所以先讲解一下什么是链 ...

随机推荐

  1. Asp.Net Core 学习随笔

    1.依赖注入 configureServices 中 //单例 services.AddSingleton<i,c>(); //http请求内 services.AddScopend< ...

  2. Apache JMeter 5.4.1 Build Development

                    1. 说明 经过漫长的等待终于将开发环境搭建成功了!网络慢真的是伤不起!grade,确实要比maven简洁.....嗯!真香! 2. 工具准备 JDK1.8+ 这... ...

  3. shell字符串处理总结

    1. 字符串切片 1.1 基于偏移量取字符串 返回字符串 string 的长度 ${#string} 示例 [root@centos8 script]#str=" I Love Python ...

  4. (原创)IconFont(矢量图标字体)在Winform中的应用

    一.前言 很多时候,使用矢量图形可以带来非常美观的界面效果,比如SVG的使用.但是Winform原生是不支持显示SVG图像的,所以退而求其次,可以使用IconFont来实现相似的矢量效果. 先来个图解 ...

  5. CIE标准色度系统(上)

    一.颜色匹配 为了满足工业生产对颜色特性的定量化和标准化的需要,由国际照明委员会(CIE)的协调和指导下,先后提出CIE1931和CIE1964标准色度观察者光谱三刺激值,由此奠定了现代色度学基础. ...

  6. MindSpore函数拟合

    技术背景 在前面一篇博客中我们介绍过基于docker的mindspore编程环境配置,这里我们基于这个环境,使用mindspore来拟合一个线性的函数,演示一下mindspore的基本用法. 环境准备 ...

  7. Spring Security OAuth2 实现登录互踢

    背景说明 一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的. 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足类似的需求. 我们先来看 To ...

  8. 多图详解 TCP 连接管理,太全了!!!

    TCP 是一种面向连接的单播协议,在 TCP 中,并不存在多播.广播的这种行为,因为 TCP 报文段中能明确发送方和接受方的 IP 地址. 在发送数据前,相互通信的双方(即发送方和接受方)需要建立一条 ...

  9. 2.7w字!Java基础面试题/知识点总结!(2021 最新版)

    这篇<Java 基础知识总结>是 JavaGuide 上阅读量最高的一篇文章,由于我对其进行了重构完善并且修复了很多小问题,所以,在博客园再同步一下! 文章内容比较多,目录如下: 基础概念 ...

  10. spring boot 通过feign调用api接口

    目的:远程调用服务器api,直接上步骤: 1,添加maven依赖,这是必须的: <dependency> <groupId>org.springframework.cloud& ...