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 ...
随机推荐
- IDA Pro使用技巧及大杂烩
IDA Pro使用技巧及大杂烩 IDA Pro基本简介 IDA加载完程序后,3个立即可见的窗口分别为IDA-View,Named,和消息输出窗口(output Window). IDA图形视图会有执行 ...
- hihoCoder #1349 Nature Numbers
题目大意 考虑自然数构成的序列 $a$:$01234567891011\dots$,序列下标从 $0$ 开始,即 $a_0 =0, a_1 = 1$ . 求 $a_n$($0\le n\le 10^{ ...
- UVa11021 Tribles
概率 递推 每只麻球都是独立计算的. 可以递推,设f[i]表示一只麻球经过i天死光的概率,那么f[i]的k次方就是k只麻球经过i天死光的概率. 则f[i]=p[0]+p[1]*f[i-1]^1+p[2 ...
- 标准C程序设计七---74
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- uniSWF使用注意事项
美术方面,也就是使用FLASH这里的用户,请注意以下几点, 1.把相同的图案做成元件: 2.凡是补间产生的动画物体,也要做成元件: 以上便可以节省大量的空间,因为当迩把图案做成元件的话,UNISWF导 ...
- react 生命周期详解
state有时候很不听话,在某些时候,我不想他渲染,偏偏react非常智能的帮我们重复渲染. 比如最常见的就是传递的对象为空,组件依旧渲染了一次或者多次. 更多场景不举例了,对症下药. shouldC ...
- AC日记——2条不相交的路径 51nod 1076
1076 2条不相交的路径 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 给出一个无向图G的顶点V和边E.进行Q次查询,查询从G的某个顶点V[s] ...
- Linux 性能优化解析
前情概述 进程调度 老板 cpu 任劳任怨的打工仔 线程 工作在做什么 可运行队列 拥有的工作清单 上下文切换 和老板沟通以便得到老板的想法并及时调整自己的工作 中断 部分工作做完以后还需要及时向老板 ...
- Codeforces Round #317 [AimFund Thanks-Round] (Div. 2) Minimization dp
原题链接:http://codeforces.com/contest/572/problem/D 题意 给你个数组A和n,k,问你排列A后,下面的最小值是多少. 题解 先排个序,要填充像1,1+k,1 ...
- xcopy中文文件名,中文件目录乱码问题
1.保存成bat脚本文件 2.且该bat文件不能使用utf-8格式,使用ANSI即正常