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 相关
随机推荐
- 如何解决WebkitBrowser使用出错“Failed to initialize activation context”
本文转载自:http://www.cnblogs.com/supjia/p/4695671.html 本篇文章主要介绍了"如何解决WebkitBrowser使用出错“Failed to in ...
- CenOS下搭建VPN服务
公司生产环境使用的是阿里云主机,采用的是两台nginx主机进行反向代理,现在需要内网一台服务器能够访问公网,所以在nginx服务器上搭建了VPN服务,用于进行内网访问公网. 系统环境:CenOS 6. ...
- VBA相关
--能否彻底隐藏某行或某列 用代码隐藏列,将其放在Private Sub Worksheet_SelectionChange(ByVal Target As Range)Columns(1).Enti ...
- (C#) 判断相等?
值类型直接用 == 号判断就好. 但是对于引用类型,需要实现IComparable 接口,或者重写 Equal 方法,来实现自己的比较目的. 因为对于引用类型,==号比较的是入口地址,对于同一个cla ...
- sql server中的左连接与右连接的简便写法
左连接 *=(左表中的数据全部显示出来,右表中没有相关联的数据显示null) select Users.*,Department.name as DepartmentName from Users,D ...
- ADF_General JSF系列2_创建JSF类型的页面向导
2015-02-17 Created By BaoXinjian
- oProfile 学习
oProfile工具可以分析CPU的负载量 只要对目标程序加上 -g 后重新编译,即可用oProfile进行分析 例如在测试apache的性能时, 增加 -g 编译选项[crifan@localhos ...
- (转)linux中fork()函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- UCOS时钟与中断:
OSTimeDly() OSTimeDlyHMSM()上面的函数除了延时功能,主要是会进入任务调度. OSTimeDlyHResume()当某条件达到之后需要停止周期性延时调用该函数. 当调用延时函数 ...
- Win7玩游戏偶尔自动跳转到桌面的解决办法[转]
新装的win7旗舰版SP1,怎么玩wow (魔兽世界.极品飞车.全屏游戏.按键精灵.挂机)总是过一会就自己返回桌面了.刚开始以为是显卡的毛病,更新了驱动还是一样(在这之前,排除病毒,其他驱动问题).因 ...