LinkedList是基于链表实现的,链表和数组是两种不同的线性物理存储结构,具体不再介绍链表的用法,这是基本的数据结构知识。LinkedList是通过双向链表的实现,即:链表中任意一个存储单元都可以通过向前或者向后寻址的方式获取到其前一个存储单元和其后一个存储单元。对于此,可看LinkedList的源码节点的定义:
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;
}
}
对于LinkedList的特性:
- LinkedList是否允许空 允许
- LinkedList是否允许重复数据 允许
- LinkedList是否有序 有序(按照插入的顺序)
- LinkedList是否线程安全 非线程安全
LinkedList的操作
由于LinkedList是由双向链表实现,且利用LinkedList封装了关于栈和队列的操作,所以里面有很多方法是封装给栈和队列来使用的。在JDK1.5后,引入了Queue接口,配合队列的操作以及并发包中队列内容的引入。
LinkedList的定义为:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
我们可以看到它实现了Deque借口,此接口表示双向队列,其继承了Queue接口,LinkedList给出其实现。所以我们在要使用队列或栈这种数据结构时,可以使用此接口来操作。
上图可以用来进行队列的基本操作,再来看Deque定义的方法:
里面定义的push(),pop()方法就是关于栈的相应操作。栈和队列获得首个元素都可以利用peek()方法获得,为什么呢?因为队列是将后入队元素放在连接在队尾,而栈定义的是将后入栈的元素连接在队头。这是双向队列实现这两种数据结构的逻辑。
另外,在JDK1.5还引入了PriorityQueue,也就是我们常用的堆数据结构,最大最小堆一般是利用数组来实现的。关于PriorityQueue的介绍,在后边的并发包学习中再详细了解。可以参看:Java PriorityQueue工作原理及实现 。
接下来分析一下LinkedList的增删查的基本原理。
添加/插入元素
我们先来看LinkedList的相关属性,可看到,有first指针和last指针,分别指向LinkedList的第一个元素和最后一个元素,符合双向链表的实现。
因为是用链表实现的,所以不存在扩容问题。
添加元素:
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element. 在链表尾部添加元素
*/
void linkLast(E e) {
//获取当前最后一个节点
final Node<E> l = last;
//新建节点
final Node<E> newNode = new Node<>(l, e, null);
//更新last
last = newNode;
//如果是第一个节点的话
if (l == null)
//就将新建节点赋值给first节点
first = newNode;
else