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 ...
随机推荐
- PostgreSQL模式匹配的方法 LIKE等
PostgreSQL 提供了三种实现模式匹配的方法:传统 SQL 的 LIKE 操作符.SQL99 新增的 SIMILAR TO 操作符. POSIX 风格的正则表达式.另外还有一个模式匹配函数 su ...
- WPF事件(一)内置路由事件
原文:WPF事件(一)内置路由事件 Windows是消息驱动的操作系统,运行其上的程序也遵照这个机制运行,随着面向对象开发平台日趋成熟,微软把消息机制封装成了更容易让人理解的事件模型,一个事件包含3个 ...
- 聊聊浏览器(webkit)资源加载机制
一些准备 在开始这个话题之前,我们有必要简单回顾一下 浏览器(webkit)的网页渲染过程(如果想要详细了解这个过程,可以戳我几年前写的一篇文章.): 我们知道,浏览器在渲染过程中,如遇到节点需要依赖 ...
- Win8Metro(C#)数字图像处理--2.3图像反色
原文:Win8Metro(C#)数字图像处理--2.3图像反色 [函数名称] 图像反色函数ContraryProcess(WriteableBitmap src) [算法说明] 反色公式如下: ...
- 毕设(三)NotifyIcon
NotifyIcon是一个比较特殊的组件,其特殊之处是既可以把它归类到控件中,也可以把它归类到组件中.这是因为将其拖放到设计窗体后,我们并不能马上看到它的界面(像组件),而是在运行时才能看到它(像控件 ...
- 该内存不能read 或written数值 叙述(居然还有具体的讲究)
该内存不能read 或written数值 叙述 0 0x0000 作业完成. 1 0x0001 不正确的函数. 2 0x0002 系统找不到指定的档案. 3 0x0003 系统找不到指定的路径. 4 ...
- Qt常见皮肤qss代码(有Metro的风格)
##QTabWidget 淡蓝色效果TabWidget(属性值lightblue) QTabWidget[lightblue = "true"] QTabBar::tab{ bor ...
- C++界面库(十几种,很全)
刚开始用C++做界面的时候,根本不知道怎么用简陋的MFC控件做出比较美观的界面,后来就开始逐渐接触到BCG Xtreme ToolkitPro v15.0.1,Skin++,等界面库,以及一些网友自 ...
- 【Linux】linux中删除指定日期之前的文件
要删除系统中就的备份文件,就需要使用命令了: #find /tmp -mtime +30 -type f -name *.sh[ab] -exec rm -f {} \; 假如在一个目录中保留最近30 ...
- 【工具】Axure 8.0 序列号
之前用的 Axure 8.0 到期最近了,重找了一个序列号,发现可用,记录一下,分享如下: 授权人:University of Science and Technology of China (CLA ...