1 LinkedList介绍
1 LinkedList继承了AbstractSequentialList,实现了接口List。是一个双向链表的实现。
2 LinkedList实现了接口Deque。表示实现了双端队列。可以FIFO。也可以LIFO(后进先出)
3 LinkedList实现了接口Cloneable、Serializable,表示可以克隆。也可以序列化。
2 属性介绍
/**
* 元素的个数
*/
transient int size = 0;
/**
* 指向第一个结点的指针
*/
transient Node<E> first;
/**
* 指向最后一个结点的指针
*/
transient Node<E> last;
3 构造启
//默认的构造器
public LinkedList() {
}
/**
* 构造一个包含元素的集合。
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
4 链表结点类结构
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;
}
}
5 源码解读
5.1 E getFirst() 获取第一个元素
/**
* 获取第一个元素。直接将头指针赋值给当前结点,然后当前结点返回存储的数据item。如果为空,则报NoSuchElementException异常。
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
5.2 E getLast() 获取最后一个元素
/**
* 获取最后一个元素。直接将尾指针赋值给当前结点,然后当前结点返回存储的数据item。如果为空,则报NoSuchElementException异常。
*/
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
5.3 E removeFirst() 删除第一个元素
/**
* 删除第一个元素。直接将头指针赋值给当前结点,然后解除结点之间的联系。调用unlinkFirst()方法接触引用
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
下面来看下unlinkFirst()方法。
private E unlinkFirst(Node<E> f) {
// 获取删除结点的数据,作为最后的返回
final E element = f.item;
//获取删除结点的下一个结点
final Node<E> next = f.next;
//将删除结点的数据和后继设置为null,有利于垃圾回收
f.item = null;
f.next = null; // help GC
//将删除结点的后继设置为头结点。
first = next;
//设置前驱为空。然后元素个数-1
if (next == null)
last = null;
else
next.prev = null;
size--;
//集合修改的次数。在迭代器迭代集合的时候使用
modCount++;
return element;
}
5.4 E removeLast() 删除最后一个元素
public E removeLast() {
//将尾结点赋值给当前结点,然后解除尾结点的引用。
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
调用unlinkLast实现了解除尾结点之间的引用。我们来看下该方法的实现。
private E unlinkLast(Node<E> l) {
final E element = l.item;
//获取删除结点的前驱
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // 方法GC
//将前驱设置为尾结点
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
5.5 addFirst(E e)、addLast(E e) 添加元素
//头插
public void addFirst(E e) {
linkFirst(e);
}
//尾插
public void addLast(E e) {
linkLast(e);
}
分别调用linkFirst和linkLast实现。我们看看这两个方法。
private void linkFirst(E e) {
//获取头结点
final Node<E> f = first;
//创建结点。设置前驱为null。后继为f。也就是插入之前的头结点
final Node<E> newNode = new Node<>(null, e, f);
//将创建的新节点赋值给头结点。
first = newNode;
if (f == null)
//如果当前结点为空,那么新结点就是尾结点。
last = newNode;
else
//将新节点设置为当前结点的前驱
f.prev = newNode;
//元素个数+1
size++;
modCount++;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
//获取尾结点
final Node<E> l = last;
//创建新结点。前驱为插入之前的尾结点。后继为空
final Node<E> newNode = new Node<>(l, e, null);
//新结点设置为尾结点
last = newNode;
if (l == null)
first = newNode;
else
//设置新节点为插入之前的后继结点
l.next = newNode;
size++;
modCount++;
}
5.6 boolean contains(Object o) 判断是否包含元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
调用indexOf()方法实现。下面看下该内部方法。
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 {
//遍历链表。如果当前对象equals结点元素,返回索引。
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
5.7 int size() 获取链表的长度
public int size() {
return size;
}
5.8 boolean add(E e) 添加元素
默认是尾插。
public boolean add(E e) {
linkLast(e);
return true;
}
5.9 boolean remove(Object o) 删除元素
public boolean remove(Object o) {
//元素为空的情况
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
//元素不为空的情况
//遍历链表,找到元素,然后解除链表结点之间的引用。调用了unlink来解除
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
unlink方法解除元素之间的引用。来看下该方法。
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;
//前驱为空,说明是头结点。那么直接将该结点的后继结点设置为头结点
if (prev == null) {
first = next;
} else {
//将当前删除结点的后继结点设置为当前删除结点的后继结点。
prev.next = next;
//并且将当前结点的前驱结点设置为空。
x.prev = null;
}
//后继结点为空,表示为尾结点
if (next == null) {
//设置前驱为尾结点
last = prev;
} else {
//后继结点不为空。将删除元素的前驱设置为后继结点的前驱。后继结点设置为空
next.prev = prev;
x.next = null;
}
x.item = null;
//元素个数-1
size--;
modCount++;
return element;
}
5.10 clear()清空集合。
遍历每一个结点,然后将所有结点元素置为空。结点置为空。集合元素个数设置为0
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}