一、前言

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的更多相关文章

  1. JDK源码分析之集合02ArrayList

    一.前言 有了前一篇对集合类的概述,我们知道ArrayList是属于Collection类系中的一个具体实现类,其特点是长度可以动态改变,集合内部使用数组保存元素.下面我们对源码进行分析. 二.Arr ...

  2. 【JDK】JDK源码分析-Vector

    概述 上文「JDK源码分析-ArrayList」主要分析了 ArrayList 的实现原理.本文分析 List 接口的另一个实现类:Vector. Vector 的内部实现与 ArrayList 类似 ...

  3. 【JDK】JDK源码分析-ArrayList

    概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」. 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变. ...

  4. 【JDK】JDK源码分析-List, Iterator, ListIterator

    List 是最常用的容器之一.之前提到过,分析源码时,优先分析接口的源码,因此这里先从 List 接口分析.List 方法列表如下: 由于上文「JDK源码分析-Collection」已对 Collec ...

  5. 【JDK】JDK源码分析-HashMap(2)

    前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理.但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题. 这里再贴一下 HashMap ...

  6. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  7. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  8. JDK源码分析(三)—— LinkedList

    参考文档 JDK源码分析(4)之 LinkedList 相关

  9. JDK源码分析(一)—— String

    dir 参考文档 JDK源码分析(1)之 String 相关

随机推荐

  1. php常见判断

    当要 判断一个变量是否已经声明的时候 可以使用 isset 函数 当要 判断一个变量是否已经赋予数据且不为空 可以用 empty 函数 当要 判断 一个变量 存在且不为空 先isset 函数 再用 e ...

  2. 好的博客mark

    1. http://blog.csdn.net/zqixiao_09/article/month/2016/03 涉及面光,包括上层/底层, uboot/kernel/rootfs/driver.

  3. system函数

    system两层含义: 1.正确退出后.还需要再判断,操作成功或者操作失败. 2.错误退出. #include <stdio.h> #include <stdlib.h> #i ...

  4. DBA_Oracle AWR Report性能监控报表(案例)

    2014-08-22 Created By BaoXinjian

  5. bug_ _ _android.app.Fragment$InstantiationException 解决办法

    在实际的开发中,我遇到过两次android.app.Fragment$InstantiationException报错. 其中一次报错,根据报错提示 “make sure class name exi ...

  6. Build常见错误

    1.ant 解决com.sun.tools.javac.Main is not on the classpath的问题 在ant打包时报错: 解决com.sun.tools.javac.Main is ...

  7. 如何让div水平居中

    代码实例: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  8. Android之BroadcastReceiver 监听系统广播

    绑定广播有两种方式 一.配置文件绑定,在程序未启动也能监听 二.代码方式绑定,在程序启动后才能监听 1.绑定和取消绑定广播 public class MainActivity extends Acti ...

  9. wpf mvvm使用问题集锦

    问题一.usercontrol1控件使用了mvvm数据绑定,usercontrol2也使用了mvvm数据绑定,则 以下是伪代码 <usercontrol2 datacontent="{ ...

  10. 那些不能错过的Xcode插件

      来源:http://www.cocoachina.com/applenews/devnews/2013/0918/7022.html 古人云“工欲善其事必先利其器”,打造一个强大的开发环境,是立即 ...