JDK源码分析之集合03LinkedList
一、前言
LinkedList是双向列表,实现方式是使用链表的方式保存元素;除了第一个和最后一个元素外,每一个节点都包含一个指向前一个和指向后一个节点的指针和元素的值。其特点是插入删除效率高,而随机访问效率低。
二、ArrayList源代码分析
2.1类的继承关系
//AbstractSequentialList实现了部分函数,Deque双端队列,支持从两端访问元素 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable |
2.2内部类
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; } } /** * Adapter to provide descending iterators via ListItr.previous */ 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(); } } |
说明:此内部类对应保存数据的节点,包含泛型类型E的当前值,和两个Node类型的属性,分别指向前一个和后一个节点。第二个为从后遍历链表的迭代器,可以通过descendingIterator()接口获得
2.3私有属性
// 链表的长度,即元素的个数 transient int size = 0; /** * Pointer to first node. Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ // 始终指向第一个元素 transient Node<E> first; /** * Pointer to last node. Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ // 始终指向最后一个元素 transient Node<E> last; |
说明:分别定义两个指向第一个元素和指向最后一个元素的指针,方便对链表的操作
2.4构造函数
/** * Constructs an empty list. */ // 创建一个空的链表 public LinkedList() { } /** * Constructs a list containing the elements of the specified collection, in * the order they are returned by the collection's iterator. */ // 创建一个包含指定集合中元素的链表 public LinkedList(Collection<? extends E> c) { this(); //调用addAll函数 addAll(c); } |
LinkedList提供了两种构造函数:创建一个空的链表和创建包含指定元素的链表,其中第二中方式是先创建一个空链表,然后再将所有元素添加到链表中,添加元素调用的addAll方法,其代码如下:
public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } |
addAll函数调用的addAll(int index,Collection<? Extends E> c);其代码如下所示:
/** * Inserts all of the elements in the specified collection into this list, * starting at the specified position. Shifts the element currently at that * position (if any) and any subsequent elements to the right (increases * their indices). The new elements will appear in the list in the order * that they are returned by the specified collection's iterator. */ public boolean addAll(int index, Collection<? extends E> c) { ,且小于本LinkedList对象的size checkPositionIndex(index); //集合转换为数组 Object[] a = c.toArray(); //要添加的新元素的个数 int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { //获取位于index的节点 succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; //新建节点其中前一个节点指向pred Node<E> newNode = new Node<>(pred, e, null); //如果index==1 if (pred == null) first = newNode; else pred.next = newNode; //将pred指向添加进去的新节点 pred = newNode; } if (succ == null) { //如果从末尾加入元素,则直接将last指向新加入的最后一个元素 last = pred; } else { //如果不是从末尾添加元素,则将新添加的最后一个节点的next指向index节点。并把index节点的prev指向新添加的最后一个节点 pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; } |
其中有调用node(int index)函数来获取指定索引位置的节点。其代码如下:
/** * Returns the (non-null) Node at the specified element index. */ Node<E> node(int index) { // assert isElementIndex(index); //如果index小于size/2则从天查找 if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { //如果index的值大于size/2则从后面查找 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } } |
2.5核心函数
、getFirst()函数
/** * Returns the first element in this list. * * @return the first element in this list */ public E getFirst() { // 返回属性first final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return f.item; } |
、unlinkFirst()函数
/** * Unlinks non-null first node f. */ private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; // 设置为null,方便垃圾回收 f.item = null; f.next = null; // help GC first = next; // 对特殊情况处理,如果只有一个元素,则last也设置为null;否则将新的首元素的prev设置为null if (next == null) last = null; else next.prev = null; size--; modCount++; return element; } |
、unlinkLast()函数
/** * Unlinks non-null last node l. */ private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; // 将要删除的节点设置为null,方便垃圾回收 l.item = null; l.prev = null; // help GC // last指向删掉的节点的前一个节点 last = prev; // 对特殊情况的处理:如果删掉的这个节点也是第一个节点,则first设置为null;否则将新的尾节点的next设置为null if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; } |
、linkFirst(E e)
/** * Links e as first element. */ private void linkFirst(E e) { // 获取first元素节点 final Node<E> f = first; // 创建新节点,next指向first节点 final Node<E> newNode = new Node<>(null, e, f); // 将first引用指向新节点 first = newNode; // 对特殊情况的处理:如果添加节点前的链表为空链表,则将last也指向新节点,否则,将之前的头节点的prev指向新的头节点 if (f == null) last = newNode; else f.prev = newNode; // 增加链表的size size++; modCount++; } |
、addLast(E e)
/** * Links e as last element. */ void linkLast(E e) { // 获取last节点的引用 final Node<E> l = last; // 创建新节点,其中prev指向last节点 final Node<E> newNode = new Node<>(l, e, null); // 将last指向新节点 last = newNode; // 对特殊情况的处理:如果添加前为空链表,则将first也指向新节点,否则将l的next指向新节点 if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } |
、indexOf(Object o)
public int indexOf(Object o) { int index = 0; if (o == null) { // 对链表遍历 for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { // 使用equals方法为判断是否相等的依据 if (o.equals(x.item)) return index; index++; } } return -1; } |
、unlink(Node<E> x)
/** * Unlinks non-null node x. */ E unlink(Node<E> x) { // assert x != null; final E element = x.item; // 分别获取要删除元素的前一个元素和后一个元素 final Node<E> next = x.next; final Node<E> prev = x.prev; // 分别对特殊情况的判断:如果删除的为头节点,将first指向next;否则将前一个节点的next指向next节点,将x的prev设置为null if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } // 如果删除的节点为尾节点,则将last指向倒数第二节点,否则,将下一个节点的prev指向前一个节点 if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; } |
、linkBefore(E e,Node<E> succ)
/** * Inserts element e before non-null Node succ. */ void linkBefore(E e, Node<E> succ) { // assert succ != null; // 先获取目标节点的前一个节点引用 final Node<E> pred = succ.prev; // 创建新节点,prev指向目标节点的前一个节点,next指向目标节点 final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; // 特殊情况的处理,如果在头节点前添加元素,则将first指向新添加的节点,否则将前一个节点的next指向新节点 if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; } |
、lastIndexOf(Object o)
public int lastIndexOf(Object o) { // 从最后一个开始遍历 int index = size; if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { index--; if (x.item == null) return index; } } else { for (Node<E> x = last; x != null; x = x.prev) { index--; if (o.equals(x.item)) return index; } } return -1; } |
、双端队列的一些函数
说明:LinkedList同时也实现了Deque接口,因此LinkedList可以用作双端队列,支持在链表两边的添加删除操作;接口列表如上图
JDK源码分析之集合03LinkedList的更多相关文章
- JDK源码分析之集合02ArrayList
一.前言 有了前一篇对集合类的概述,我们知道ArrayList是属于Collection类系中的一个具体实现类,其特点是长度可以动态改变,集合内部使用数组保存元素.下面我们对源码进行分析. 二.Arr ...
- 【JDK】JDK源码分析-Vector
概述 上文「JDK源码分析-ArrayList」主要分析了 ArrayList 的实现原理.本文分析 List 接口的另一个实现类:Vector. Vector 的内部实现与 ArrayList 类似 ...
- 【JDK】JDK源码分析-ArrayList
概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」. 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变. ...
- 【JDK】JDK源码分析-List, Iterator, ListIterator
List 是最常用的容器之一.之前提到过,分析源码时,优先分析接口的源码,因此这里先从 List 接口分析.List 方法列表如下: 由于上文「JDK源码分析-Collection」已对 Collec ...
- 【JDK】JDK源码分析-HashMap(2)
前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理.但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题. 这里再贴一下 HashMap ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable
JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...
- JDK源码分析(三)—— LinkedList
参考文档 JDK源码分析(4)之 LinkedList 相关
- JDK源码分析(一)—— String
dir 参考文档 JDK源码分析(1)之 String 相关
随机推荐
- windows下 更新 android studio SDK 到最新版本 解决方案
一.设置代理信息 打开android studio>>File>>Settings>>Appearance&Behavion>>System S ...
- php安装redis扩展
1.windows安装redis扩展 php_redis.dll (for windows) 的下载地址:http://windows.php.net/downloads/pecl/snaps/red ...
- MongoDB源码概述——内存管理和存储引擎
原文地址:http://creator.cnblogs.com/ 数据存储: 之前在介绍Journal的时候有说到为什么MongoDB会先把数据放入内存,而不是直接持久化到数据库存储文件,这与Mong ...
- MVC:上传文件
今天写了一个使用MVC上传的DEMO,很简单不超过10行代码.代码如下(关注重点,所以尽量精简掉其他代码): 项目结构
- C# ASP.NET 开发指引简要
推荐学习交流社区:博客园http://www.cnblogs.com/ 里面有很多技术.职业圈子等信息的分享,新手必逛社区. 电子书等资源下载:csdn下载 http://download.csdn. ...
- CF #365 (Div. 2) D - Mishka and Interesting sum 离线树状数组(转)
转载自:http://www.cnblogs.com/icode-girl/p/5744409.html 题目链接:CF #365 (Div. 2) D - Mishka and Interestin ...
- ylbtech-dbs:ylbtech-6,record(生活记录)
ylbtech-dbs:ylbtech-6,record(生活记录) -- =============================================-- DatabaseName:R ...
- spring ioc原理(看完后大家可以自己写一个spring)
控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专 ...
- C++学习17派生类的构造函数
基类的构造函数不能被继承,在声明派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数来完成.所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要考虑基类的成员变量,要让它们都 ...
- [ActionScript 3.0] AS3 3D双圆环贴图
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.MovieCl ...