java源码学习(五)LinkedList
LinkedList
一、定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
- LinkedList 实现 List 接口,能对它进行队列操作。
- LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
- LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
- LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
- LinkedList 是非同步的。
// 相关类的继承和实现接口关系
public abstract class AbstractSequentialList<E> extends AbstractList<E>
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
public abstract class AbstractCollection<E> implements Collection<E>
public interface List<E> extends Collection<E>
public interface Collection<E> extends Iterable<E>
public interface Deque<E> extends Queue<E>
public interface Queue<E> extends Collection<E>
二、数据结构
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实现双向链表,Node静态内部类
三、属性
LinkedList提供了3个私有属性,一个protected属性
// 双向链表的节点数量
transient int size = 0;
// 首节点(链表头节点)
// 需要满足条件:(first == null && last == null) ||
// (first.prev == null && first.item != null)
// 首尾节点都为null或者
// 首节点不为null且首节点的前节点为null
transient Node<E> first;
// 尾节点
// 需要满足条件:(first == null && last == null) ||
// (last.next == null && last.item != null)
// 首尾节点都为null或者
// 尾节点不为null且尾节点的后节点为null
transient Node<E> last;
//protected 链表元素变更记录数,主要用于Fast-Fail机制
protected transient int modCount = 0;
- 四条属性都是transient,不参与序列化
四、构造方法
LinkedList提供了2个构造方法,一个默认无参构造,一个传入集合构造方法
// 构造一个空节点列表
public LinkedList() {
}
// 构造一个包含Collection c元素列表,这些元素按照Collection遍历的顺序添加到LinkedList中
// Collection c为null,抛出NullPointerException
public LinkedList(Collection<? extends E> c) {
this();
// 下面有详细介绍该方法,该方法就是把c中所有元素添加到LinkedList链表尾部
addAll(c);
}
五、方法详细介绍
1. 元素添加
关于LinkedList元素的添加,提供了以下方法(双端队列方法下面有单独介绍):
public boolean add(E e)// 添加元素到尾部
public boolean addAll(Collection<? extends E> c)// 尾部按顺序添加所有集合元素
public void add(int index, E element) // 指定位置添加元素
public boolean addAll(int index, Collection<? extends E> c) // 指定位置按顺序添加所有集合元素
链表元素添加有四个基本通用方法linkFirst、linkLast、linkBefore以及node方法
//头插入,即将节点值为e的节点设置为链表首节点
void linkFirst(E e) {
final Node<E> f = first;
//构建一个prev值为null,节点值为e,next值为f的新节点newNode
final Node<E> newNode = new Node<>(null, e, f);
//将newNode作为首节点
first = newNode;
//如果原首节点为null,即原链表为null,则链表尾节点也设置为newNode
if (f == null)
last = newNode;
else //否则,原首节点的prev设置为newNode
f.prev = newNode;
size++;
modCount++;
}
// 尾插入,即将节点值为e的节点设置为链表的尾节点
void linkLast(E e) {
// 先保存原首节点
final Node<E> l = last;
// 构建一个prev值为l,节点值为e,next值为null的新节点newNode
final Node<E> newNode = new Node<>(l, e, null);
// 将newNode作为尾节点
last = newNode;
// 如果原尾节点为null,即原链表为null,则链表首节点也设置为newNode
if (l == null)
first = newNode;
else // 否则,原尾节点的next设置为newNode
l.next = newNode;
// 链表节点数增加并且链表变更次数增加
size++;
modCount++;
}
// 中间插入,在非空节点succ之前插入节点值e
// 流程就是 1)先创建一个节点,节点元素为e,原succ的前节点现在是新节点的前节点,新节点的后节点为succ节点 2)修改succ的前节点为新节点 3)原succ的前节点是否为空处理,为空则新节点为首节点,不为空原succ的前节点的后节点修改为新节点
void linkBefore(E e, Node<E> succ) {
// 前提是succ不为null
// 保存succ节点的前节点
final Node<E> pred = succ.prev;
// 创建newNode节点,将newNode的后继指针指向succ,前驱指针指向pred
final Node<E> newNode = new Node<>(pred, e, succ);
// 将succ的前驱指针指向newNode
succ.prev = newNode;
// 判断succ节点的前节点是否为null
if (pred == null)
// 该节点插入在头节点之前,要重置first头节点
first = newNode;
else
// 直接将pred的后继指针指向newNode
pred.next = newNode;
// 链表节点数增加并且链表变更次数增加
size++;
modCount++;
}
// 返回指定位置的节点
Node<E> node(int index) {
// 保证所有入口的index都是合法的范围
// 判断索引是否靠近链表的前半部分
if (index < (size >> 1)) {
// 从头开始遍历
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 从尾开始遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
// 通用index校验,一个是修改index校验,一个是获取index校验,区别是 index <= size 还是index < size
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
add(E e)方法,添加元素到尾节点
public boolean add(E e) {
linkLast(e);
return true;
}
add(int index, E element)方法,指定位置添加元素
public void add(int index, E element) {
// 校验index范围;index >= 0 && index <= size,非范围抛出异常
checkPositionIndex(index);
if (index == size)
linkLast(element);// 链表尾部添加
else
linkBefore(element, node(index));// 链表中间添加
}
addAll(Collection<? extends E> c)方法,尾部按顺序添加所有集合元素(AbstractList)
// c为null 会抛出NullPointerException
public boolean addAll(Collection<? extends E> c) {
// 调用下面指定位置的添加方法,index从size开始
return addAll(size, c);
}
addAll(int index, Collection<? extends E> c)方法,指定位置按顺序添加所有集合元素(AbstractList)
// c为null 会抛出NullPointerException
public boolean addAll(int index, Collection<? extends E> c) {
// 校验index范围;index >= 0 && index <= size,非范围抛出异常
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
// 若新增集合为空集合,则直接返回false
if (numNew == 0)
return false;
Node<E> pred, succ;// succ指向当前需要插入节点的位置,pred指向其前一个节点
if (index == size) {// 尾部添加
succ = null;
pred = last;
} else {
succ = node(index);// 获取索引所在的节点
pred = succ.prev;// 索引节点的前节点
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 将元素值e,前继节点pred“封装”为一个新节点newNode
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)// 如果索引节点的前节点为null,则新插入的节点作为链表的首节点
first = newNode;
else
pred.next = newNode;// 索引节点的前节点 的后节点 设置为newNode
// pred 指针向后移动
pred = newNode;
}
// 元素列表插入完成后,需要连接index后面的元素
if (succ == null) {// 说明之前是在尾部插入元素
last = pred;
} else {
pred.next = succ; // 列表最后一个元素的节点的后继指向原index位置的节点
succ.prev = pred;// 原index位置的节点的前驱指向列表最后一个元素的节点
}
// 链表节点数增加并且链表变更次数增加
size += numNew;
modCount++;
return true;
}
2. 元素获取及查找
关于LinkedList元素获取及查找,提供了以下方法:
public E get(int index)// 获取索引所在节点
public E set(int index, E element)// 修改索引所在的元素
public int indexOf(Object o)// 获取元素所在的第一个出现的位置
public int lastIndexOf(Object o)// 获取元素所在的最后一个出现的位置
public boolean contains(Object o)// 判断链表是否包含元素
public E get(int index) {
// 校验index范围,错误抛出异常
checkElementIndex(index);
return node(index).item;
}
public E set(int index, E element) {
// 校验index范围,错误抛出异常
checkElementIndex(index);
// 获取索引所在节点,元素值替换,并返回原有节点的元素
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
public int indexOf(Object o) {
int index = 0;
// 从前往后遍历,找到元素o,返回索引下标
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) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
public int lastIndexOf(Object o) {
int index = size;
// 从后往前遍历,找到元素o,返回索引下标
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;
}
// 判断链表是否包含元素o
public boolean contains(Object o) {
return indexOf(o) != -1;
}
3. 元素删除
关于LinkedList元素的删除,提供了以下方法:
public E remove(int index)
public boolean remove(Object o)
public void clear()
先了解一下删除元素的基本通用方法(删除首节点、尾节点、中间节点)
// 删除首节点 gc
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
// 删除尾节点 gc
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
// 删除指定节点
E unlink(Node<E> x) {
// 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节点 所有属性置为空,gc回收
x.item = null;
// 节点数-1,操作数+1
size--;
modCount++;
// 返回删除节点的元素
return element;
}
remove(int index)方法,删除指定位置的元素
public E remove(int index) {
// 校验index范围,错误抛出异常
checkElementIndex(index);
return unlink(node(index));
}
remove(Object o)方法,删除数组中第一个为o的元素
// 顺序遍历双向链表,找到元素删除并return
// 没找到元素,返回false
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 {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
clear()方法,删除列表中所有的元素
public void clear() {
// 遍历双向链表,置空节点所有属性,gc回收
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++;
}
4. 序列化与反序列化
LinkedList的序列化重写了writeObject和readObject方法;
transient不被序列化
writeObject
// 将对象实例写入输出流,序列化
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 默认序列
s.defaultWriteObject();
// 写入size
s.writeInt(size);
// 链表顺序写入节点元素
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
readObject
// 从输入流反写出对象实例(反序列化)
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 默认
s.defaultReadObject();
// 读入size
int size = s.readInt();
// 读入元素,并把元素添加到链表的尾部
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}
5. LinkedList转化为数组
Object[] toArray()
public Object[] toArray() {
// 先创建链表大小的Object数组
Object[] result = new Object[size];
int i = 0;
// 遍历双向链表,节点元素赋值给数组
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
**<T> T[] toArray(T[] a) ** 泛型转化
public <T> T[] toArray(T[] a) {
// 参数a容量不够,则反射扩容到size
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
// 若a的容量大于size,则size的元素全部为null
if (a.length > size)
a[size] = null;
return a;
}
6. Queue方法
// 队列接口定义
public interface Queue<E> extends Collection<E> {
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
}
LinkedList对于Queue方法实现:
public boolean add(E e) {
linkLast(e);
return true;
}
// 将指定的元素值(E e)插入此列表末尾
public boolean offer(E e) {
return add(e);
}
// 获取并移除此队列的头,如果此队列为空,则抛出NoSuchElementException异常
public E remove() {
return removeFirst();
}
// 获取并移除此队列的头,如果此队列为空,则返回 null
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
// 获取但不移除此队列的头;如果此队列为空,则抛出NoSuchElementException异常
public E element() {
return getFirst();
}
// 获取但不移除此队列的头;如果此队列为空,则返回 null
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
7. Deque方法(双端队列)
双端队列Deque继承了Queue
public interface Deque<E> extends Queue<E> {
// Dueue队列方法
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
// Queue队列方法
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
// Stack方法
void push(E e);
E pop();
// Collection方法
boolean remove(Object o);
boolean contains(Object o);
public int size();
Iterator<E> iterator();
Iterator<E> descendingIterator();
}
// 将指定的元素插入此双端队列的开头
public void addFirst(E e) {
linkFirst(e);
}
// 将指定的元素插入此双端队列的末尾
public void addLast(E e) {
linkLast(e);
}
// 将指定的元素插入此双端队列的开头
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
// 将指定的元素插入此双端队列的末尾
public boolean offerLast(E e) {
addLast(e);
return true;
}
// 移除此双端队列的第一个元素;如果此双端队列为空,则抛出NoSuchElementException异常
public E removeFirst() {
final Node<E> f = first;
// 删除的链表不能为空
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
// 移除此双端队列的最后一个元素;如果此双端队列为空,则抛出NoSuchElementException异常
public E removeLast() {
final Node<E> l = last;
// 删除的链表不能为空
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
// 获取,但不移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
// 获取,但不移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
// 获取并移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
// 获取并移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
// 获取队列的头;如果此队列为空,则抛出NoSuchElementException异常
public E getFirst() {
final Node<E> f = first;
// 如果首节点为null,抛出异常
if (f == null)
throw new NoSuchElementException();
return f.item;
}
// 获取队列的尾;如果此队列为空,则抛出NoSuchElementException异常
public E getLast() {
final Node<E> l = last;
// 如果尾节点为null,抛出异常
if (l == null)
throw new NoSuchElementException();
return l.item;
}
// 将一个元素推入此双端队列所表示的堆栈(换句话说,此双端队列的头部)
public void push(E e) {
addFirst(e);
}
// 从此双端队列所表示的堆栈中弹出一个元素(换句话说,移除并返回此双端队列的头部)
public E pop() {
return removeFirst();
}
// 从此双端队列移除第一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
// 从此双端队列移除最后一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
public boolean removeLastOccurrence(Object o) {
// 由于LinkedList中允许存放null,因此下面通过两种情况来分别处理
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {// 逆向向前
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
8. 迭代器
LinkedList 提供了三个迭代器构造方法:
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
可以看出来最终都是调用listIterator(int index)方法,参数表示是迭代器开始的位置,下面来看ListItr代码(实现双向迭代器)
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
// 保存当前modCount,可以确保Fast-Fail机制
private int expectedModCount = modCount;
ListItr(int index) {
// 确保index范围正确
// 初始化迭代器的开始节点
next = (index == size) ? null : node(index);// 获取index的next节点
nextIndex = index;
}
// 是否还有下一个元素节点
public boolean hasNext() {
return nextIndex < size;
}
// 获取下一个元素节点
public E next() {
// Fast-Fail校验
checkForComodification();
// 是否还有下一个元素,没有则抛出NoSuchElementException异常
if (!hasNext())
throw new NoSuchElementException();
// next节点向后移动
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
// 获取当前节点是否还有上一个节点
public boolean hasPrevious() {
return nextIndex > 0;
}
public E previous() {
// Fast-Fail校验
checkForComodification();
// 是否还有上一个元素,没有则抛出NoSuchElementException异常
if (!hasPrevious())
throw new NoSuchElementException();
// next节点向前移动
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
// 可以看出lastReturned 若是next操作,next后移则lastReturned位于next之前;
// 若是previous,lastReturned与next是相等的
// 删除lastReturned节点
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
// 设置lastReturned节点的元素为e
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
// lastReturned节点后面添加元素e
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
// Fast-Fail校验
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
下面我们看看迭代器的用法,不建议使用随机访问的方式进行遍历:
// 通过迭代器Iterator遍历
Iterator iter = list.iterator();
while (iter.hasNext())
{
System.out.println(iter.next());
}
// 通过迭代器ListIterator遍历
ListIterator<String> lIter = list.listIterator();
//顺向遍历
while(lIter.hasNext()){
System.out.println(lIter.next());
}
//逆向遍历
while(lIter.hasPrevious()){
System.out.println(lIter.previous());
}
//foreach循环遍历
for(String str:list)
{
System.out.println(str);
}
LinkedList还提供了一个完全倒序遍历的Iterator(DescendingIterator),还是用ListItr实现的
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
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();
}
}
JDK1.8还提供一个Spliterator接口,这里就不讲解了,有兴趣的可以自己看一下
9. 其他方法
public int size() {
return size;
}
// LinkedList的copy
public Object clone() {
LinkedList<E> clone = superClone();
// Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
五、参考资料
http://blog.csdn.net/qq_19431333/article/details/54572876
java岛上程序猿,十指弯弯不弄弦。
终日立于电脑前,不问今夕是何年。
但行好事,莫问前程。
泰山崩于前,我依然沐浴更衣焚香沏茶。
诚心正意,手起键落。
人生如戏, 全靠演技。
Hello World !
作者:人生如戏, 全靠演技
出处:http://www.cnblogs.com/yy1024/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。