Java源码--LinkedList源码概述

与ArrayList同为List,LinkedList却展现出不同的特性。作为java.util下的另一重要容器,我们下面来探究一下LinkedList的源码实现及特性分析。

上篇文章讲述到,ArrayList用数组来存储数据,伴随数据量的变大,ArrayList动态扩充数组容量
与之不同,LinkedList使用链表来存储数据,因此它在插入/删除数据方面有着天然的优势,而在读取指定位置的元素时,性能却不及ArrayList速度快

LinkedList存储元素的变量如下:

transient Node<E> first;

  存储数据的节点: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; //后节点
	}
}

 可以看出来,所有元素节点连在一起,构成了双向链表(prev指向前一个元素节点,next指向后一个元素节点)。所有数据,保存在Node的item中。相比于单向链表,双向链表提供了两种操作的方向,因此可以提供更快的遍历查找速度,更快的插入、删除元素速度(代价是提升了链表维护的难度,以及一点点存储引用的空间)。

下面看一下LinkedList内部对于双向链表的维护:


在此之前,需要知道的一点:LinkedList额外保存了first和last两个节点,分别指向首位和末尾,作为双向链表的两端入口。

内部维护链表的一系列函数如下(private、protected):

//用e构造Node,将数据插到首位
private void linkFirst(E e)
{
	final Node<E> f = first; //取得首位节点node
	final Node<E> newNode = new Node<>(null, e, f); //构造节点,item为e,prev为null,next指向当前的首位f
	first = newNode; //将first指向newNode,以它作为新的首位
	if (f == null)
		last = newNode; //若之前的首位f不存在,则新的首位节点newNode在作为first的同时,也是last
	else
		f.prev = newNode; //若之前的首位f存在,则将f的prev指向新的首位节点newNode
	size++;
	modCount++;
}

//用e构造Node,将数据插到末尾,原理类似
void linkLast(E e);

//将节点插入到指定节点前
void linkBefore(E e, Node<E> succ)
{
	final Node<E> pred = succ.prev; //取得原先succ之前的节点pred,现在需要将newNode插在succ之前,pred之后
	final Node<E> newNode = new Node<>(pred, e, succ); //构造内容为e的newNode,prev指向pred,next指向succ
	succ.prev = newNode; //将succ的prev指向newNode
	if (pred == null)
		first = newNode; //若pred为空,则将newNode置为first
	else
		pred.next = newNode; //将pred的next指向newNode
	size++;
	modCount++;
}

//将首位first节点从链表去掉
private E unlinkFirst(Node<E> f);

//将末位last节点从链表去掉
private E unlinkLast(Node<E> l);

//将任意位置的节点x从链表去掉
E unlink(Node<E> x)
{
	//分别考虑prev与next为null和非null的情况,修复prev与next的指向
}

  基于上述链表操作函数,LinkedList开放了如下接口(public)

public E getFirst()
{
	//取得first内部的item,返回
}

public E getLast()
{
	//取得last内部的item返回
}

public E removeFirst()
{
	//调用unlinkFirst(Node<E> f), 将first从链表移除
}

public E removeLast()
{
	//调用unlinkLast(Node<E> l), 将last从链表移除
}

public void addFirst(E e)
{
	//将e插入到first之前
}

public void addLast(E e)
{
	//将e插入到last之后
}

public boolean add(E e)
{
	//调用linkLast(e),将e插入到last之后
}

public boolean remove(Object o)
{
	//从first开始遍历链表,找到o,移除节点
}


public int indexOf(Object o)
{
	//从first开始遍历节点,找到o,返回index
}

public int lastIndexOf(Object o)
{
	//从last开始反向遍历链表,找到o,移除链表
}

public boolean contains(Object o)
{
	//从first开始遍历链表,找到o,返回true;或者找不到o,返回false
}

public boolean addAll(Collection<? extends E> c)
{
	//1. 将集合c转成数组
	//2. 遍历数组,对于每个元素,构造node,链在last后面
}

  

总结LinkedList用双向链表维护元素,相比于ArrayList提供了快速的插入、移除数据操作的同时,也比单向链表的遍历查找速度更快。但是,相比于ArrayList,LinkedList的查找指定index元素效率低(ArrayList使用数组存储数据,可以直接依据索引读取)。

猜你喜欢

转载自www.cnblogs.com/xinxinBlog/p/9937651.html