LinkedList源码分析:JDK源码分析系列
如果本文中有不正确的地方请指出
由于没有留言可以在公众号添加我的好友共同讨论。
1.介绍
LinkedList 是线程不安全的,允许元素为null的双向链表。
2.继承结构
我们来看一下LinkedList的继承结构图:
代码实现:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- Cloneable实现克隆
- Serializable序列化
- List 定义了一些集合类的方法
- Deque双向队列接口(就是两端都可以进行增加删除操作)
注意一点LinkedList并没有实现RandomAccess所以随机访问是非常慢的。
3.属性
元素个数
transient int size = 0;
指向第一个节点的指针(注释直接就写着)
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
指向最后一个节点的指针
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
transient关键字的作用是保持变量不被序列化具体的百度。
不过我们注意到LinkedList是有Node类,这里的Node是一个内部类:
- item存储的元素
- next指向后置节点
- prev指向前置节点
- 内部类同时包含了一个构造函数
其实LinkedList 集合就是由许多个 Node 对象y一个连着一个组成手构成,可以看下方的图
可以看上面的四个元素,分别就是四个Node对象,可以看到node1所指向的prev上一个节点是空也就是没有上节点,node4所指向的next节点为空也就是没有下一个节点。
4.构造方法
LinkedList共有两个构造方法,一个是创建一个空的构造函数,一个是将已有的Collection添加到LinkedList中。
因为LinkedList不同于ArrayList所以初始化不需要指定大小取分配内存空间。
5.添加元素
LinkedList有几种添加方法我们先从add()开始。
5.1 add方法
checkPositionIndex(index);
可以看到在调用Add方法首先校验一下索引是否合法,如果不合法就会抛出异常。
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
然后如果索引值等于size的值则直接调用linkLast插入到尾部节点。
否则就linkBefore方法。
linkLast方法:
void linkLast(E e) {
//将l设为尾节点
final Node<E> l = last;
//构造一个新节点,节点上一个节点引用指向尾节点l
final Node<E> newNode = new Node<>(l, e, null);
//将尾节点设为创建的新节点
last = newNode;
//如果尾节点为空,表示原先链表为空
if (l == null)
//将头节点设为新创建的节点(尾节点也是新创建的节点)
first = newNode;
else
//将原来尾节点下一个节点的引用指向新节点
l.next = newNode;
size++;//节点数加1
modCount++;
}
注意一下这里出现了modCount方法,和ArrayList中一样,iterator和listIterator方法返回的迭代器和列表迭代器实现使用。
linkBefore:
void linkBefore(E e, Node<E> succ) {
//将pred设为插入节点的上一个节点
final Node<E> pred = succ.prev;
//将新节点的上引用设为pred,下引用设为succ
final Node<E> newNode = new Node<>(pred, e, succ);
//succ的上一个节点的引用设为新节点
succ.prev = newNode;
//如果插入节点的上一个节点引用为空
if (pred == null)
//新节点就是头节点
first = newNode;
else
//插入节点的下一个节点引用设为新节点
pred.next = newNode;
size++;
//同上
modCount++;
}
5.2 addAll()
在LinkedList中有两个addAll方法一个是 addAll(Collection<? extends E> c)一个是addAll(int index, Collection<? extends E> c),其实
addAll(Collection<? extends E> c)=addAll(int index, Collection<? extends E> c)
可以看下面的截图:
源码如下:
现在开始分析源码:
首先我们可以看到先调用了checkPositionIndex(index);方法来检查索引是否合法。
然后将传进来的Collection转成Object数组
Object[] a = c.toArray();
如果集合为空就直接返回false
if (numNew == 0)
return false;
如果插入的位置等于链表的长度就把新增加的元素放在尾部。
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
最后在循环要插入的元素。这里我们可以看到上面也有modCount。
5.3 addFirst()
addFirst是将元素插入到LinkedList的头部。
如果现在的机构是下图的:
addFirst执行的操作就是:
红色是使用addFirst插入后的位置。一下是源码
调用了linkFirst方法。
上面的操作时:
- 首先执行final Node f = first;将头节点赋值给 f
- final Node newNode = new Node<>(null, e, f);将指定元素构造成一个新节点,此节点的指向下一个节点的引用为头节点
//将新节点设为头节点,那么原先的头节点 f 变为第二个节点
first = newNode;
//如果第二个节点为空,也就是原先链表是空
if (f == null)
//将这个新节点也设为尾节点(前面已经设为头节点了)
last = newNode;
else
f.prev = newNode;//将原先的头节点的上一个节点指向新节点
size++;//节点数加1
modCount++;//和ArrayList中一样记录修改次数
5.4 addLast方法
addLast是将元素插入到尾部如图(黄色元素):
黄色node是新增加。注意add也是把元素添加到尾部。我们来看一下源码:
这里看到addLast和add是调用的同一方法,这里就不在讲解。可以看上方的代码。
由于文章比较长分为两次更新。下一篇文章继续分析
上次分析了LinkedList的结构和添加方法这次开始分析下面的。
注意源码版本为JDK1.8
直接进入正题。
1.删除
1.2remove()
在LinkedList中remove()和removeFirst()是相同的
在LinkedList中的删除其实就是通过修改上一个节点和指向下一个节点的引用完成的,可以看下面的图片:
我们可以看到把node6的prev指向清空然后把node3的next设置成空就完成了删除的操作。
看一下JDK的源码实现:
调用removeFirst,在调用removeFirst中先获取头部节点,如果头节点为空就抛异常。
调用unlinkFirst
private E unlinkFirst(Node<E> f) {
final E element = f.item;
//next 为头结点的下一个节点
final Node<E> next = f.next;
f.item = null;
// 将节点的元素以及引用都设为 null,便于垃圾回收
f.next = null;
//修改头结点为第二个节点
first = next;
//如果第二个节点为空(当前链表只存在第一个元素)
if (next == null)
//那么尾节点也置为 null
last = null;
else
//如果第二个节点不为空,那么将第二个节点的上一个引用置为 null
next.prev = null;
size--;
modCount++;
return element;
}
1.3 removeLast()
我们看一下removeLast,从列表中删除最后一个元素
首先看到先获取了last最后一个元素,如果为空然后就抛异常
然后调用了unlinkLast
看到unlinkLast方法中有一个 help gc ,它的主要作用就是帮助GC来做垃圾回收。
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
//帮助GC垃圾回收
l.prev = null;
//尾节点为倒数第二个节点
last = prev;
//如果倒数第二个节点为null
if (prev == null)
//那么将节点也置为 null
first = null;
else
prev.next = null;
//如果倒数第二个节点不为空,那么将倒数第二个节点的下一个引用置为 null
size--;
modCount++;
return element;
}
同样执行了modCount++
1.3 remove(int index)
根据索引来删除元素
//删除此列表中指定位置的元素
public E remove(int index) {
//判断索引 index >= 0 && index <= size中时抛出IndexOutOfBoundsException异常
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) {//如果删除节点位置的上一个节点引用为null(表示删除第一个元素)
first = next;//将头结点置为第一个元素的下一个节点
} else {//如果删除节点位置的上一个节点引用不为null
prev.next = next;//将删除节点的上一个节点的下一个节点引用指向删除节点的下一个节点(去掉删除节点)
x.prev = null;//删除节点的上一个节点引用置为null
}
if (next == null) {//如果删除节点的下一个节点引用为null(表示删除最后一个节点)
last = prev;//将尾节点置为删除节点的上一个节点
} else {//不是删除尾节点
next.prev = prev;//将删除节点的下一个节点的上一个节点的引用指向删除节点的上一个节点
x.next = null;//将删除节点的下一个节点引用置为null
}
//删除节点内容置为null,便于垃圾回收
x.item = null;
size--;
modCount++;
return element;
}
3.set(int index, E element)
简单粗暴直接贴代码
public E set(int index, E element) {
//检查索引是否合法否则IndexOutOfBoundsException异常
checkElementIndex(index);
//获取指定索引的元素
Node<E> x = node(index);
E oldVal = x.item;
//将指定索引的元素替换成新的元素
x.item = element;
/返回指定索引位置原来的元素
return oldVal;/
}
4.查找元素
很简单
4.1 getFirst()
返回第一个元素
public E getFirst() {
获取头
final Node<E> f = first;
判断是否为空
if (f == null)
throw new NoSuchElementException();
元素返回
return f.item;
}
4.2 getLast()
返回最后一个元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
4.3 get
返回指定索引元素
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
暂时就这么多
原创不易,如果你喜欢本文或者对你有帮助就请转发
LinkedList源码分析:JDK源码分析系列的更多相关文章
- HashSet源码分析:JDK源码系列
1.简介 继续分析源码,上一篇文章把HashMap的分析完毕.本文开始分析HashSet简单的介绍一下. HashSet是一个无重复元素集合,内部使用HashMap实现,所以HashMap的特征耶继承 ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- 【jdk源码分析】ArrayList的size()==0和isEmpty()
先看结果 分析源码 [jdk源码解析]jdk8的ArrayList初始化长度为0 java的基本数据类型默认值 无参构造 size()方法 isEmpty()方法
- eclipse查看jdk源码,及反编译查看
jdk中的包: dt.jar是关于运行环境的类库,主要是swing的包 tools.jar是关于一些工具的类库 rt.jar包含了jdk的基础类库,也就是你在java doc里面看到的所有的类的cla ...
- 关于JDK源码:我想聊聊如何更高效地阅读
简介 大家好,我是彤哥,今天我想和大家再聊聊JDK源码的几个问题: 为什么要看JDK源码 JDK源码的阅读顺序 JDK源码的阅读方法 为什么要看JDK源码 一,JDK源码是其它所有源码的基础,看懂了J ...
- 一言不合就开始搞JDK源码
Java是一门面向对象的编程语言,那什么是面向对象呢,下面将是历史上最通俗易懂的解释了,请看下图: 哈哈,解释的够清楚的了吧.闪. 从源码学编程的好处 学Java编程时,最好同时看一些Java的源码 ...
- JDK源码分析(三)—— LinkedList
参考文档 JDK源码分析(4)之 LinkedList 相关
- JDK源码分析(2)LinkedList
JDK版本 LinkedList简介 LinkedList 是一个继承于AbstractSequentialList的双向链表.它也可以被当作堆栈.队列或双端队列进行操作. LinkedList 实现 ...
- JDK源码分析(三)——HashMap 上(基于JDK7)
目录 HashMap概述 内部字段及构造方法 存储元素 扩容 取出元素 删除元素 判断 总结 HashMap概述 前面我们分析了基于数组实现的ArrayList和基于双向链表实现的LinkedLi ...
随机推荐
- ef core code first from exist db
目标 为现有数据库生成新的连接,允许只选择部分表 可以处理一些很怪的需求,比如EF升级EF Core(这个可能有其他解),EF.EF Core同时连接一个数据库 我遇到的问题是: 原项目是.net f ...
- 《Docker 实战》第三章 Docker Hub 寻宝游戏
# 秘密仓库和密码 docker run --rm -it --name password dockerinaction/ch3_ex2_huntanswer
- 【转】关于List排序的时效性
不多说了,就是说明List排序的时效性,仅仅用来备忘,改造自: http://blog.csdn.net/wanzhuan2010/article/details/6205884,感谢原作者 usin ...
- python 编码转换 专题
主要介绍了python的编码机制,unicode, utf-8, utf-16, GBK, GB2312,ISO-8859-1 等编码之间的转换. 常见的编码转换分为以下几种情况: 自动识别 字符串编 ...
- scala 学习
继续学习: https://segmentfault.com/a/1190000003068853#articleHeader2 https://docs.scala-lang.org/tour/mi ...
- Advanced Installer 安装前卸载旧版本的办法
原文:Advanced Installer 安装前卸载旧版本的办法 Advanced Installer这个工具百度出来的资料太少了. 在我们平常打包的工作中,经常遇到的一个问题是,如何能在安装新版本 ...
- ManualResetEvent 让你的代码等你几分钟
using System;using System.Collections.Generic;using System.Linq;using System.Threading; namespace Co ...
- Mysql下载(on windows-noinstall zip archive)
所有内容,都是针对Mysql5.7.18介绍. 1.首先你需要下载一个完整的包,Mysql目前有两个版本可以使用: a. MySql Enterprise Edition:企业版 b. MySql C ...
- 使用百度网盘+Git,把版本控制托管到云端,附精彩评论
http://www.cnblogs.com/vajoy/p/3929675.html 我试过多个这种双向同步的网盘,在网络状况不好.系统卡顿以及某些程序BUG的情况下,同步会有错乱现象,尤其是多个电 ...
- Qt 5 最小构建笔记(只编译QtBase)
只想用Qt5最基本的功能,因此只编译QtBase.也不想为了编译一个Qt装很多东西(比如非常肥的DirectX SDK) 软件清单: Visual Studio 2010 Professional w ...