LinkedList 是 List 的又一种实现方法,首先看一下它的类图:
LinkedList 继承自 AbstractSequentialList, 实现了Deque、Cloneable、Serializable 接口,同 ArrayList 一样,它包含了AbstractList 的所有的行为特征,但它的实现方式是链表,而且是双向链表。
1、成员变量
LinkedList 提供了四个成员变量
transient int size = 0; // LinkedList 的容量
transient Node //代码效果参考:http://www.jhylw.com.cn/385825795.html
first; // 第一个节点transient Node last; // 最后一个节点
protected transient int modCount = 0; // List结构化修改的次数(size大小发生改变的操作都会增加)
可以看到,四个变量都使用了 transient 进行修饰,在序列化的时候这三个变量不会序列化。
Node 是 LinkedList 的私有内部类,实现代码如下:
private static class Node {
E item; // 当前节点的元素
Node next; // 后一节点的引用地址
Node prev; // 前一节点的引用地址
Node(Node prev, E element, Node next) {
// 节点初始化时会带有三个参数,前一节点引用、当前节点元素和后一节点引用
this.item = element;
this.next = next;
this.prev = prev;
}
}
2、构造函数
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
LinkedList 提供了两个构造函数,一个是无参数构造函数;另一个是带有 Collection 类型参数的构造函数。可以看到,与ArrayList不同,LinkedList 并不需要设置初始容量。
3、实现自Deque的方法
Deque 是一个队列接口,表明该类的实现类是一个双向队列,实现了 Queue。Queue 提供了 add(E e)、offer(E e)、remove()、poll()、element()、peek() 方法,Deque 提供了addFirst(E e)、addLast(E e)、offerFirst(E e)、offerLast(E e)、removeFirst()、removeLast()、pollFirst()、pollLast()、getFirst()、getLast()、peekFirst()、peekLast()等。
Queue 提供的方法主要是队列头部取出以及尾部的加入方法,是FIFO(先入先出)的数据结构;Deque 在队列的头部和尾部均可以获取、添加、以及删除。下面分析一下具体方法的实现:
(1)add(E e) 方法
public boolean add(E e) { // 在尾部添加一个元素,等同于 addLast(E e)
linkLast(e);
return true;
}
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node(l, e, null);
last = newNode;
if (l == null) // 加入之前,last 未初始化
first = newNode; // 初始化 first,此时first = last
else
l.next = newNode;
size++; // 元素数量+1
modCount++; // 修改次数+1
}
(2)offer(E e) 方法 其实使用的是add方法
public boolean offer(E e) {
return add(e);
}
(3) remove() 方法
public E remove() { // 删除队列的第一个接地那
return removeFirst();
}
public E removeFirst() {
final Node f = first;
if (f == null) // first 未初始化,表示队列里无数据
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node f) { // 删除第一个相等的元素
// assert f == first && f != null;
final E element = f.item;
final Node next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null) // 即此时first=null时,队列里没有元素,last也置为null
last = null;
else
next.prev = null; // 前驱节点释放
size--; // 队列元素数量-1
modCount++; // 结构化修改数量+1
return element;
}
(4)poll() 方法
public E poll() { // 删除队列头部的第一个元素并返回
final Node f = first;
return (f == null) ? null : unlinkFirst(f);
}
poll() 方法和 remove() 方法差不多,都是使用 unlinkFirst(E e) 删除头部的第一个元素,但是和 remove(E e) 不同的是,在队列为空的情况下调用 poll() 会返回 null,而 remove() 方法会抛出 NoSuchElementException 异常(其实是使用removeFirst(E e) 方法抛出的异常)。
(5)element() 方法
public E element() { // 返回头部节点的元素
return getFirst();
}
public E getFirst() {
final Node f = first;
if (f == null) // 队列为空时,抛出异常
throw new NoSuchElementException();
return f.item;
}
(6)peek() 方法
public E peek() {
final Node f = first;
return (f == null) ? null : f.item;
}
peek() 和 element() 方法都是返回队列的头部节点,队列为空时,peek() 会返回 null ,element() 会抛出 NoSuchElementException 异常。
以上是Queue 提供的方法,其实还有 Collection 当中的方法,后面再介绍,下面介绍 Dueue 的方法。
(1)addFirst(E e)
public void addFirst(E e) { // 在队列的头部添加元素
linkFirst(e);
}
private void linkFirst(E e) {
final Node f = first;
final Node newNode = new Node(null, e, f);
first = newNode;
if (f == null) // 队列为空时,新加入节点是last,first为null
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
(2)addLast(E e)
public void addLast(E e) { // 等同于add(E e)和offer(E e),只是没有返回值
linkLast(e);
}
(3)offerFirst(E e)
public boolean offerFirst(E e) { // 等同于addFirst(E e),有返回值
addFirst(e);
return true;
}
(4)offerLast(E e)
public boolean offerLast(E e) { // 等同于addLast(E e),有返回值
addLast(e);
return true;
}
(5)push(E e)
public void push(E e) {
addFirst(e);
}
(6)pop(E e)
public E pop() { // 删除队列头部元素并返回,队列为空会抛出异常
return removeFirst();
}
4、其他方法
(1)get(int index)方法
public E get(int index) { // 获取指定位置的元素
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index)) // 判断是否 0<=index
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index [span style="color: rgba(0, 0, 0, 1)"> size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
Node node(int index) { // 这个才是真正的获取指定位置元素的方法,获取的是引用
// assert isElementIndex(index);
if (index < (size ] 1)) { // index < size/2,从头部开始遍历,否则从尾部开始遍历
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
(2)set(int index,E element)
public E set(int index, E element) {
checkElementIndex(index);
Node x = node(index); // 将获取到指定位置的引用返回
E oldVal = x.item;
x.item = element;
return oldVal;
}
(3)add(int index, E element)
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element); // 从尾部添加元素
else
// 获取指定位置元素的引用,其前驱节点改为新元素
linkBefore(element, node(index));
}
void linkBefore(E e, Node succ) {
// assert succ != null;
final Node pred = succ.prev;
final Node newNode = new Node(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
(4)indexOf(Object o)
public int indexOf(Object o) { // 从头部遍历,返回头一个等于输入参数的元素位置
int index = 0;
if (o == null) { // 为 null 时,判断元素等于null,否则调用equals判断元素相等
for (Node x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
(5)itrator() 方法
LinkedList 中本身没有 itrator() 的方法体,方法体在其父类 AbstractSequentialList 中,它实际使用的也是 AbstractList 的实现。
(6)listItrator() 方法
当然 ListedList 作为 AbstractList 的子孙类,同样提供了 listItrator() 方法,既可以向前遍历,也可以向后遍历。其返回类型 Iterator 的实现方式也是内部类:
// 无参数的实现方式在 AbstractList 中已经实现
public ListIterator listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator {
private Node lastReturned;
private Node next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index); // 下一节点位置
nextIndex = index;
}
public boolean hasNext() {
return nextIndex [span style="color: rgba(0, 0, 0, 1)"> size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {
return nextIndex > 0<span style="color: rg