概要

  • 类继承关系
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
{
}
  • 要点

  1. 双链表
  2. 非同步
  3. 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();
}
}

这个非常有意思,直接把 nexthasNext 函数设置为 previous 即可了。非常大程度上降低了代码。

OpenJDK 源码阅读之 LinkedList的更多相关文章

  1. 转-OpenJDK源码阅读导航跟编译

    OpenJDK源码阅读导航 OpenJDK源码阅读导航 博客分类: Virtual Machine HotSpot VM Java OpenJDK openjdk 这是链接帖.主体内容都在各链接中.  ...

  2. openjdk源码阅读导航

    转自:http://rednaxelafx.iteye.com/blog/1549577 这是链接帖.主体内容都在各链接中. 怕放草稿箱里过会儿又坑掉了,总之先发出来再说…回头再慢慢补充内容. 先把I ...

  3. 源码阅读之LinkedList(JDK8)

    inkedList概述 LinkedList是List和Deque接口的双向链表的实现.实现了所有可选列表操作,并允许包括null值. LinkedList既然是通过双向链表去实现的,那么它可以被当作 ...

  4. jdk源码阅读笔记-LinkedList

    一.LinkedList概述 LinkedList的底层数据结构为双向链表结构,与ArrayList相同的是LinkedList也可以存储相同或null的元素.相对于ArrayList来说,Linke ...

  5. java1.7集合源码阅读:LinkedList

    先看看类定义: public class LinkedList<E> extends AbstractSequentialList<E> implements List< ...

  6. openjdk源码阅读

    http://rednaxelafx.iteye.com/blog/1549577 http://blog.csdn.net/fancyerii/article/details/7007503 ├—a ...

  7. 【JDK1.8】JDK1.8集合源码阅读——LinkedList

    一.前言 这次我们来看一下常见的List中的第二个--LinkedList,在前面分析ArrayList的时候,我们提到,LinkedList是链表的结构,其实它跟我们在分析map的时候讲到的Link ...

  8. 【源码阅读】Java集合之二 - LinkedList源码深度解读

    Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章; 本文是第二篇LinkedList. ---@pdai JDK版 ...

  9. java8 ArrayList源码阅读

    转载自 java8 ArrayList源码阅读 本文基于jdk1.8 JavaCollection库中有三类:List,Queue,Set 其中List,有三个子实现类:ArrayList,Vecto ...

随机推荐

  1. python风格之包导入

    导入总应该放在文件顶部, 位于模块注释和文档字符串之后, 模块全局变量和常量之前. 导入应该按照从最通用到最不通用的顺序分组: 标准库导入 第三方库导入 应用程序指定导入 每种分组中, 应该根据每个模 ...

  2. webstorm自带debugger服务器

    打开webstorm->settings->Build,Execution,Deployment->Debugger->把端口Port改成8089或者其他80端口,按确定就可以 ...

  3. HDU——1213How Many Tables(并查集按秩合并)

    J - How Many Tables Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u ...

  4. bzoj 3190 [JLOI2013]赛车 半平面交+细节处理

    题目大意 这里有一场赛车比赛正在进行,赛场上一共有N辆车,分别称为g1,g2--gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进ki的位置.比赛开始后,车辆gi将会以vi单位每秒的恒定速度行 ...

  5. bzoj 2801 [Poi2012]Minimalist Security 设一个,求出所有

    题目大意 给出一个N个顶点.M条边的无向图,边(u,v)有权值w(u,v),顶点i也有权值p(i), 并且对于每条边(u,v)都满足p(u)+p(v)>=w(u,v). 现在要将顶点i的权值减去 ...

  6. iframe平铺到浏览器

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  7. 最有用的java面试题

    1.什么是线程局部变量?(答案) 线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享.Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式.但是 ...

  8. buffer和cache怎么让你们解释的那么难理解?

    对于一个即将踏上“系统运维”或者更加高大尚的工作“系统调优”,如果这不跟这两哥们搞好关系了,坑的不只有内存,更坑的是你拿着调优的钱却干着随时被调的活.因为作为一个系统运维人员来说监控和优化IO性能这是 ...

  9. Linux 之 网络相关设置

    网络相关设置 参考教程:[千峰教育] 命令: ping: 作用:通常用于检测网络设备的连通性. 格式:ping IP/域名 选项:-c,指定方式测试数据包的次数 实例:ping www.baidu.c ...

  10. POJ - 2135最小费用流

    题目链接:http://poj.org/problem?id=2135 今天学习最小费用流.模板手敲了一遍. 产生了一个新的问题:对于一条无向边,这样修改了正向边容量后,反向边不用管吗? 后来想了想, ...