LinkedList原理及源码解析
简介
LinkedList是一个双向线性链表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
UML关系图
使用示例
LinkedList list = new LinkedList<>();
//新增
list.add("a");
list.add("a");
list.add("b");
System.out.println(list);
//删除
list.remove("a");//删除第一个对应对象
list.remove(0);//删除下标为0的元素
list.removeFirst();//删除第一个元素
list.removeLast();//删除最后一个元素
System.out.println(list);
//修改
list.set(0,"c");//修改下标为0的元素
System.out.println(list);
//插入
list.add(1,"a");//只能在已有元素的前后或中间位置插入
System.out.println(list);
//获取
list.get(0);//根据下标获取元素
list.getFirst();//获取第一个元素
list.getLast();//获取最后一个元素
//循环
//普通循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//foreach循环
for(Object o:list){
System.out.println(o);
}
//迭代循环
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
源码解析
初始化
节点
//链表的核心类-节点
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;
}
}
构造方法
//默认大小
transient int size = 0;
//第一个节点
transient Node<E> first;
//最后一个节点
transient Node<E> last;
//无参构造
public LinkedList() {
}
//带有初始化集合构造
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
addAll的分析
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);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//当一个节点的时候last和当前的节点相同
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
//更新链表的长度
size += numNew;
modCount++;
return true;
}
常用函数分析
增加
//增加元素,默认向链表的结尾增加节点
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
//获取链表尾部节点
final Node<E> l = last;
//创建新的节点,将最后的节点做为新节点的上一个节点引用,元素为传入元素
final Node<E> newNode = new Node<>(l, e, null);
//链表尾部指向最新节点
last = newNode;
//如果原来尾部节点为空,说明为空链表需要设置链表头部节点
//否则将原来链表尾部节点的next指向新的节点
if (l == null)
first = newNode;
else
l.next = newNode;
//增加大小
size++;
//记录修改次数,主要用于集合迭代,保证迭代数据的准确性
//在迭代时候如果集合发生变化则会抛出异常
//throw new ConcurrentModificationException();
modCount++;
}
删除
//根据下标进行删除节点
public E remove(int index) {
//检测下标是否越界
checkElementIndex(index);
return unlink(node(index));
}
//获取index的节点
Node<E> node(int 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;
}
}
//删除第一个节点
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//断开第一个节点
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
//将当前节点的下个节点设为第一个节点
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
//删除最后一个节点
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//断开最后一个节点
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
//将最后一个节点设为当前节点的上一个节点
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
修改(替换)元素
//修改某个元素
public E set(int index, E element) {
//检测下标是否越界
checkElementIndex(index);
//根据下标获取节点
Node<E> x = node(index);
//获取节点的元素
E oldVal = x.item;
//赋值新的元素
x.item = element;
return oldVal;
}
插入元素
//插入指定位置元素
public void add(int index, E element) {
//检测下标是否越界
checkPositionIndex(index);
//判断下标是否为最后一个位置,如果是直接插入到最后一个节点
//否则查询出当前index的节点,将新的节点放到原来index节点之前
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//插入index节点之前位置
void linkBefore(E e, Node<E> succ) {
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++;
}
获取元素
//根据下标获取元素
public E get(int index) {
//检测下标是否越界
checkElementIndex(index);
//根据下标获取节点,然后返回节点的元素
return node(index).item;
}
//获取第一个元素
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
//获取最后一个元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
清除所有数据
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
//将所有数据进行逐个赋值为null,帮助gc的垃圾回收
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
//记录清除的操作,保证迭代的准确性
modCount++;
}
总结
- 优点
1、插入和移除数据效率高 2、链表形式方便做为栈或队列场景使用 3、相对arraylist来说,linkedlist无需扩容 - 缺点
1、根据随机获取元素效率低(根据下标获取)
2、批量添加集合效率低
其他
在arraylist和linkedlist中的全局标量都出现了一个修饰符transient,此修饰符的属性默认是不能够被序列化的,jdk作者为了减少空间的浪费所以实现了writeObject(ObjectOutputStream)和readObject(ObjectInputStream)这两个方法,进行手动的序列化有存储数据的有用属性。
参考
更多请关注微信………
LinkedList原理及源码解析的更多相关文章
- Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试
机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...
- Spring-Session实现Session共享实现原理以及源码解析
知其然,还要知其所以然 ! 本篇介绍Spring-Session的整个实现的原理.以及对核心的源码进行简单的介绍! 实现原理介绍 实现原理这里简单说明描述: 就是当Web服务器接收到http请求后,当 ...
- Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析
1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- 【转】Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- 有关LinkedList常用方法的源码解析
上文里解析了有关ArrayList中的几个常用方法的源码——<有关ArrayList常用方法的源码解析>,本文将对LinkedList的常用方法做简要解析. LinkedList是基于链表 ...
- Redux异步解决方案之Redux-Thunk原理及源码解析
前段时间,我们写了一篇Redux源码分析的文章,也分析了跟React连接的库React-Redux的源码实现.但是在Redux的生态中还有一个很重要的部分没有涉及到,那就是Redux的异步解决方案.本 ...
- ORB原理与源码解析
转载: http://blog.csdn.net/luoshixian099/article/details/48523267 CSDN-勿在浮沙筑高台 没有时间重新复制代码,只能一股脑的复制,所以代 ...
随机推荐
- win10+ubuntu17.04双系统安装与卸载(uefi+gpt)及常见软件安装
换了台笔记本,重新装了下系统.配置是i7 6700hq+gtx965m,重装了下双系统. 之前老机器是大一时候买的,装得是win7+ubuntu19.04(mbr+bios).新机器到手,想法是装了w ...
- Java图书管理系统(用Java常用集合实现)
图书管理系统 一.需求说明 1.功能:登录,注册,忘记密码,管理员管理,图书管理. 2.管理员管理:管理员的增删改查. 3.图书管理:图书的增删改查. 4.管理员属性包括:id,姓名,性别,年龄,家庭 ...
- java集合(1)- 类底层数据结构分析
Java 集合类图 参考:http://www.cnblogs.com/xwdreamer/archive/2012/05/30/2526822.html
- nginx源码分析——线程池
源码: nginx 1.13.0-release 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影 ...
- mysql之 mysqldump 备份恢复详解
mysqldump是MySQL用于转存储数据库的客户端程序.转储包含创建表和/或装载表的SQL语句 ,用来实现轻量级的快速迁移或恢复数据库,是mysql数据库实现逻辑备份的一种方式. mysqldum ...
- 全景智慧城市——VR全景,开启VR营销新时代
全景是一种新兴的富媒体技术. 与视频.声音.图片等传统主流媒体最大的区别是"可操作,可交互". 全景给人以三维立体感觉的实景360°全方位图像,此图像最大的三个特点: 全方位:展示 ...
- Win95+IE3 – Win10+IE11全版本执行漏洞(含POC)
微软本月安全更新修复了一个潜藏了18年的IE远程代码执行漏洞(CVE-2014-6332),可以说是给windows吃了一颗大补丸.缺陷出现在VBScript的代码中,自Windows 95首次发布( ...
- linux平台下Hadoop下载、安装、配置
在这里我使用的linux版本是CentOS 6.4 CentOS-6.4-i386-bin-DVD1.iso 下载地址: http://mirrors.aliyun.com/cen ...
- mongoDB数据库的简单使用
我的第一篇小文章,以前总是写Evernote. mongodb属于非关系型数据库中的文档型数据库. 1.下载安装mongoDB, 文件自动 存放在这个目录下:C:\Program Files\Mong ...
- 搭建rtmp直播流服务之1:使用nginx搭建rtmp直播流服务器(nginx-rtmp模块的安装以及rtmp直播流配置)
欢迎大家积极开心的加入讨论群 群号:371249677 (点击这里进群) 一.方案简要 首先通过对开发方案的仔细研究(实时监控.流媒体.直播流方案的数据源-->协议转换-->服务器--&g ...