OpenJDK 源码阅读之 LinkedList
概要
- 类继承关系
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.AbstractSequentialList<E>
java.util.LinkedList<E>
- 定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
}
要点
- 双链表
- 非同步
- fail-fast: 即创建 iterator 后,对链表进行了加入,删除。可是却没实用 iterator 的方法。就会抛出 ConcurrentModificationException 异常。
实现
- 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;
}
}
双链表每一个结点由 Node 类表示, 由其成员能够看出,确实是一个双链表。
- add
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
add 操作会在表的最后加入一个元素,调用 linkLast 实现。
- LinkLast
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
linkLast 先生成新的结点。再将原来的最后结点的 next 指向新结点,须要注意链表为空时的情况。另外,新结点的 prev,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;
}
}
- serialVersionUID
private static final long serialVersionUID = 876323262645176354L;
序列化版本号号,假设前一版本号序列化后。后一版本号发生了非常大改变,就使用这个号告诉虚拟机,不能反序列化了。
- writeObject
比例一下 ArrayList 与 LinkedList 中的
writeObject
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject(); // Write out array length
s.writeInt(elementData.length); // Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]); if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
} }
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject(); // Write out size
s.writeInt(size); // Write out all elements in the proper order.
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
注意后者没有检查 modCount,这是为什么呢?之前看 ArrayList的时候认为是为线程安全考虑的,但是如今为什么又不检查了呢?尽管两个文件的凝视中都说到,假设有多个线程操作此数据结构。应该从外部进行同步。但是一个检查,一个不检查是几个意思呀?
- 维护数据结构一致性
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
注意代码第 5行对 f 的检查,6 行对 last 的调整。一定要细心,保证操作后。 全部可能 涉及的数据都得到对应更新。
- 隐藏实现
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
注意返回的是数据。而不是Node,外部根本不须要知道 Node 的存在。
另外,为什么 f == null 要抛出异常而不是返回null?
- 为什么要分成两个函数
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
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 removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
删除操作分成两个函数,这是为什么呢?还有其他的一些操作也是这样。能想到的是其他操作可能也须要用到 unlinkFirst。
- LinkedList 中以 index 检索
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;
}
}
能够看出这里的小技巧,以 index 在前半段还是后半段,来决定是从前向后搜索,还是从后向前。
- 代码反复问题
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
好奇这两个函数为什么会同一时候存在。Google到,原来是为了实现不同的接口,所以须要同一时候存在这两个函数,类似的情况还存在。
- DescendingIterator
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
这个非常有意思,直接把 next, hasNext 函数设置为 previous 即可了。非常大程度上降低了代码。
OpenJDK 源码阅读之 LinkedList的更多相关文章
- 转-OpenJDK源码阅读导航跟编译
OpenJDK源码阅读导航 OpenJDK源码阅读导航 博客分类: Virtual Machine HotSpot VM Java OpenJDK openjdk 这是链接帖.主体内容都在各链接中. ...
- openjdk源码阅读导航
转自:http://rednaxelafx.iteye.com/blog/1549577 这是链接帖.主体内容都在各链接中. 怕放草稿箱里过会儿又坑掉了,总之先发出来再说…回头再慢慢补充内容. 先把I ...
- 源码阅读之LinkedList(JDK8)
inkedList概述 LinkedList是List和Deque接口的双向链表的实现.实现了所有可选列表操作,并允许包括null值. LinkedList既然是通过双向链表去实现的,那么它可以被当作 ...
- jdk源码阅读笔记-LinkedList
一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...
- java1.7集合源码阅读:LinkedList
先看看类定义: public class LinkedList<E> extends AbstractSequentialList<E> implements List< ...
- openjdk源码阅读
http://rednaxelafx.iteye.com/blog/1549577 http://blog.csdn.net/fancyerii/article/details/7007503 ├—a ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedList
一.前言 这次我们来看一下常见的List中的第二个--LinkedList,在前面分析ArrayList的时候,我们提到,LinkedList是链表的结构,其实它跟我们在分析map的时候讲到的Link ...
- 【源码阅读】Java集合之二 - LinkedList源码深度解读
Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章; 本文是第二篇LinkedList. ---@pdai JDK版 ...
- java8 ArrayList源码阅读
转载自 java8 ArrayList源码阅读 本文基于jdk1.8 JavaCollection库中有三类:List,Queue,Set 其中List,有三个子实现类:ArrayList,Vecto ...
随机推荐
- RabbitMQ的应用场景以及基本原理介绍(转)
1.背景 RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue)的开源实现. 2.应用场景 2.1异步处理 场景说明:用户注册后,需要发注册邮件和注册短信, ...
- eclipse中添加svn插件
在eclipse中使用svn查看能非常方便的对代码进行查看和更新提交操作,能及时知道代码的更新状态. 在eclipse中如果要使用svn,只能使用svn插件的方式进行. 插件地址:http://sub ...
- visual studio NuGet 常用包管理命令
visual studio NuGet 常用包管理命令 查找包 Find-Package [包名] Find-Package [包名] -AllVersions 安装包 Install-Package ...
- Java-线索二叉树的实现
概念性的东西,自行百度. 按照国际管理,直接上代码来分析. 1.Node节点类 package com.tree.thread; /** * Author: lihao * Date:2017/8/3 ...
- SEO总结(一)
- poj 1410 Intersection 线段相交
题目链接 题意 判断线段和矩形是否有交点(矩形的范围是四条边及内部). 思路 判断线段和矩形的四条边有无交点 && 线段是否在矩形内. 注意第二个条件. Code #include & ...
- C/C++ XMPP/Jabber 客户端类库对比/点评 (转)
原文转自 http://blog.csdn.net/educast/article/details/31359835 1.gloox Ans. 老牌库,推荐 gloox是一个稳定功能完整的XMPP客户 ...
- NYOJ90 整数划分(经典递归和dp)
整数划分 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 将正整数n表示成一系列正整数之和:n=n1+n2+…+nk, 其中n1≥n2≥…≥nk≥1,k≥1. 正 ...
- Netty中NioEventLoopGroup的创建源码分析
NioEventLoopGroup的无参构造: public NioEventLoopGroup() { this(0); } 调用了单参的构造: public NioEventLoopGroup(i ...
- android中添加只有border-left的样式
如何在android中的边框添加只有左边边框有颜色的样式呢 1. 相应的drawable文件 <?xml version="1.0" encoding="utf-8 ...