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 ...
随机推荐
- [笔记]BFS算法的python实现
#!/usr/bin/env python # -*- coding:utf-8 -*- graph = {} graph["you"] = ["alice", ...
- IO Streams:扫描
简介 Scanner类被用于输入的格式化中断,并将其移到Tokens中,然后对其单个的Tokens根据其数据类型进行翻译. 从input--Tokens 默认情况下,一个Scanner使用 空格 键去 ...
- lambda遍历的精简
本文转自 http://it.deepinmind.com/java%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/2014/03/15/Java%E5%8 ...
- UVA12206 Stammering Aliens 【SAM 或 二分 + hash】
题意 求一个串中出现至少m次的子串的最大长度,对于最大长度,求出最大的左端点 题解 本来想练哈希的,没忍住就写了一个SAM SAM拿来做就很裸了 只要检查每个节点的right集合大小是否不小于m,然后 ...
- 解决 Jackson反序列化 Unexpected token ... , expected VALUE_STRING: need JSON String that contains type id (for subtype of ...)
首先检查是否是 objectMapper.enableDefaultTyping(); 的受害者.优先考虑删除该配置. 使用Jackson把数组的json字符串反序列化为List时候报了个JsonMa ...
- bzoj 2791 [Poi2012]Rendezvous 基环森林
题目大意 给定一个n个顶点的有向图,每个顶点有且仅有一条出边. 对于顶点i,记它的出边为(i, a[i]). 再给出q组询问,每组询问由两个顶点a.b组成,要求输出满足下面条件的x.y: 从顶点a沿着 ...
- Java-堆排序
public class Main { public static void main(String[] args) { int a[] = {8, 2, 5, 6, 4, 8, 9, 7, 14, ...
- 搭建redis集群时所遇问题及解决方法
单独一台虚拟机(系统CentOS 7) 问题1 创建redis集群环境时,输入以下命令 [root@localhost redis-cluster]# ./redis-trib.rb create - ...
- vSphere Client 更改 ESX/ESXi 主机的端口
https://blog.csdn.net/hanzheng260561728/article/details/51283808?locationNum=8&fps=1
- Codeforces 946 B.Weird Subtraction Process
B. Weird Subtraction Process time limit per test 1 second memory limit per test 256 megabytes inpu ...