【Java源码分析】LinkedList源码分析
类的定义如下
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
- 本质是一个双向链表,实现了List和Deque接口。实现了所有List的操作,可以存储所有类型的对象,包括NULL
- 非线程安全多线程调用的时候必须通过外部保证线程安全性,所有的添加或者删除操作也必须保证线程安全性。通常的实现方法是通过对象锁的形式保证线程安全性,或者通过
Collections.synchronizedList
实现,例如List list = Collections.synchronizedList(new LinkedList(...));
- 迭代器进行遍历的时候同样存在
fail-fast
现象,可以参考ArrayList分析中的描述 - 由于是
AbstractSequentialList
的子类,同时本质是双向链表,所以只能顺序访问集合中的元素 - 和ArrayList一样也是支持序列化操作的
成员变量
transient int size = 0; // 集合中对象个数
transient Node<E> first; // 指向第一个对象的指针
transient Node<E> last; // 指向最后一个对象的指针
构造函数有两个,一个是无惨默认构造,一个是根据传递的集合类构造一个包含指定元素的链表。相对于ArrayList而言,LinkedList的构造要简单很多,因为不涉及到容量的变化
public LinkedList() {}public LinkedList(Collection<? extends E> c) {this();addAll(c);
}
从头尾添加对象
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;elsef.prev = newNode;size++;modCount++;
}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;elsel.next = newNode;size++;modCount++;
}
其中所用的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;}
}
该类的主要就是一个构造函数,直接指定前驱和后继,new的时候就将指针链接完成。相应的还有unlinkFirst和unlinkLast操作,以及linkBefore和linkAfter操作,以及unlink(Node n)操作,操作都是基本的双链表操作。这些操作的共同点在于操作都是针对集合中的对象而言,所以只需要修改链表就可以了。
删除操作(和之前不同之处在于这里给定的参数并不是集合中的对象而是一个值与集合中对象的节点的item值相同)
public boolean remove(Object o) {if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) {unlink(x);return true;}}} else {for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) {unlink(x);return true;}}}return false;
}
和前面的方法相比,这种知道值删除集合对象的操作就是一个遍历查找的过程,由于是链表实现,所以就是一个单链表或者双链表的遍历加上修改指针的操作
清空集合操作
public void clear() {// Clearing all of the links between nodes is "unnecessary", but:// - helps a generational GC if the discarded nodes inhabit// more than one generation// - is sure to free memory even if there is a reachable Iteratorfor (Node<E> x = first; x != null; ) {Node<E> next = x.next;x.item = null;x.next = null;x.prev = null;x = next;}first = last = null;size = 0;modCount++;
}
这个函数重点部分在注释中已经说明了,对于链表类释放的时候节点间的指针并不是必须要释放的,但是释放了可以帮助GC有效的回收,也可以保证彻底的释放内存,即使是存在迭代器(由于clear()操作更改了LinkedList的结构,所以指向它的迭代器会因为ConcurrentModification而fail-fast)
返回指定下标的节点
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 peek() {final Node<E> f = first;return (f == null) ? null : f.item;
}public E element() {return getFirst();
}public E poll() {final Node<E> f = first;return (f == null) ? null : unlinkFirst(f);
}
三个方法都可以取首节点的item值,但是前两个只是取出并不删除,但是第三个函数返回首节点item并且删除首节点
添加节点
public boolean offer(E e) {return add(e);
}public boolean offerFirst(E e) {addFirst(e);return true;
}public boolean offerLast(E e) {addLast(e);return true;
}
第一个添加就是在链表的尾部添加,后面两个添加操作一个是添加到链表的首部一个是添加到尾部,这两个方法属于是双端队列Dequeue的操作
两个很类似栈的方法
public void push(E e) {addFirst(e);
}public E pop() {return removeFirst();
}
Push和Pop都是针对链表的首部元素而言,所以LinkedList不仅可以当做一个双端队列使用,也可以当做一个栈来使用
和ArrayList一样,LinkedList内部类迭代器也同样需要记录modCount,所以也会出现fail-fast
private class ListItr implements ListIterator<E> {private Node<E> lastReturned = null;private Node<E> next;private int nextIndex;private int expectedModCount = modCount;
.....
}
扮演集合Collection与数组Array之间转换桥梁的函数
public Object[] toArray() {Object[] result = new Object[size];int i = 0;for (Node<E> x = first; x != null; x = x.next)result[i++] = x.item;return result;
}@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {if (a.length < size)a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);int i = 0;Object[] result = a;for (Node<E> x = first; x != null; x = x.next)result[i++] = x.item;if (a.length > size)a[size] = null;return a;
}
第二个是泛型版本,将LinkedList内容转换为指定运行时类型的一个数组存储起来。这个方法比第一个方法的优势在于可以动态确定类型,而且可以减少allocation的代价
序列化和反序列化(状态的保存和读取)
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out any hidden serialization magics.defaultWriteObject();// Write out sizes.writeInt(size);// Write out all elements in the proper order.for (Node<E> x = first; x != null; x = x.next)s.writeObject(x.item);
}@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject();// Read in sizeint size = s.readInt();// Read in all elements in the proper order.for (int i = 0; i < size; i++)linkLast((E)s.readObject());
}
同样需要注意写入顺序必须和读取顺序对应。
【Java源码分析】LinkedList源码分析相关推荐
- Java集合篇:LinkedList源码分析
(注:本文内容基于JDK1.6) 一.概述: LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口 ...
- Java类集框架 —— LinkedList源码分析
在JDK1.7之前,LinkedList是采用双向环形链表来实现的,在1.7及之后,Oracle将LinkedList做了优化,将环形链表改成了线性链表.本文对于LinkedList的源码分析基于JD ...
- 从面试角度分析LinkedList源码
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 Linke ...
- Java集合(二二): LinkedList源码剖析
目录 1.LinkedList简介 2.LinkedList数据结构 3.ArrayList源码分析LinkedList3.ArrayList源码分析 3.1 ArrayList继承结构和层次关系Li ...
- java(7)LinkedList源码
系统环境 JDK1.7 LinkedList的基本结构 :在JDK1.6中LinkedList是双向引用的环形结构,JDK1.6中是双向引用的线性结构 提醒:看链表代码时最好用笔画下链表结构 有助于理 ...
- java linkedlist源码_Java集合之LinkedList源码分析
一.LinkedList简介 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. ps:这里有一个问题,就是关于实现LinkedList的数据结构是否 ...
- Java集合类框架源码分析 之 LinkedList源码解析 【4】
上一篇介绍了ArrayList的源码分析[点击看文章],既然ArrayList都已经做了介绍,那么作为他同胞兄弟的LinkedList,当然必须也配拥有姓名! Talk is cheap,show m ...
- LinkedList 源码分析
前言 上篇文章分析了 ArrayList 的源码,面试过程中经常会被面试官来问 LinkedList 和 ArrayList 的区别,这篇文章从源码的角度来看下 LinkedList 以后,再和上篇文 ...
- LinkedList 源码分析(JDK 1.8)
1.概述 LinkedList 是 Java 集合框架中一个重要的实现,其底层采用的双向链表结构.和 ArrayList 一样,LinkedList 也支持空值和重复值.由于 LinkedList 基 ...
- java hashset 源码_Java集合源码分析-HashSet和LinkedHashSet
前两篇文章分别分析了Java的ArrayList和LinkedList实现原理,这篇文章分析下HashSet和LinkedHashSet的源码.重点讲解HashSet,因为LinkedHashSet是 ...
最新文章
- .NET应用三层架构分析
- 设置centos6.5虚拟机时间同步
- 如何吸收分数c语言,用C语言编程平均分数
- 开启 J2EE(七)— Model1、Model2和三层架构的演变
- Android NDK学习笔记3:JNI访问Java属性、方法
- 使用ReportStudio打开cube模型创建报表出现两个最细粒度名称
- html下拉框设置默认值_如何设置HTML select下拉框的默认值?
- Java EE 7发布–反馈和新闻报道
- Win32ASM 变长参数的理解
- MySql 查询显示
- Linux内核 eBPF基础: 探索USDT探针
- BootStrap table 传递搜索参数
- emmx用xmind打开_XMind: ZEN 2020 正式更名 XMind 2020!新名字,新招式。
- partial、struct、interface与C#和CLR的关系
- zsh: command not found: 解决方法
- IE浏览器无法通过ftp:\\192.168.xxx.xxx连接ftp服务器
- handler回调主线程_Handler源码和9个常见问题的解答,这些你都掌握了吗?
- 小白都懂的Python爬虫之网易云音乐下载
- spss多元线性回归散点图_SPSS19.0实战之多元线性回归分析
- 人工智能数学基础1:三角函数的定义、公式及固定角三角函数值
热门文章
- python获取用户输入中文_python中的用户输入
- 0x80070003系统找不到指定路径_DeepFaceLab错误:DLL Load failed 找不到指定模块!
- 由于找不到appvisvsubsystems32.dll_去固始张街逛一逛,以后可能看不到了......
- Python进阶8——字典与散列表,字符串编解码
- AD域中NTP服务器的配置
- 在JAVA读取Excel数据的日期格式
- 部署Exchange Server 2007 SCC
- HBase slave 启动不了的一个原因处理
- nginx处理web请求分析
- 时间:2014年3月27日文件和目录操作函数