Java8——LinkedList源码详解

LinkedList

Java8——LinkedList源码详解_第1张图片
LinkedList是通过一个双向链表来实现的,它允许插入所有元素,包括null,它是线程不同步的

双向链表的结构如下图
Java8——LinkedList源码详解_第2张图片
双向链表每个节点除了数据域以外,还有一个前指针和后指针,分别指向前驱节点和后继节点,另外,双向链表还有一个first指针,指向头结点,last指针指向尾结点。

属性

//链表的节点个数
transient int size = 0;
//指向头节点的指针
transient Node<E> first;
//指向尾节点的指针
transient Node<E> last;

方法

节点结构

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;
	 } 
 }

Node 是在 LinkedList 里定义的一个静态内部类,它表示链表每个节点的结构,包括一个数据域 item,一个后置指针 next,一个前置指针 prev。

添加元素

对于链表这种数据结构来说,添加元素的操作无非就是在表头/表尾插入元素,又或者在指定位置插入元素。因为 LinkedList 有头指针和尾指针,所以在表头或表尾进行插入元素只需要 O(1) 的时间,而在指定位置插入元素则需要先遍历一下链表,所以复杂度为 O(n)。

表头添加元素过程
Java8——LinkedList源码详解_第3张图片
当向表头插入一个节点时,很显然当前节点的前驱一定为 null,而后继结点是 first 指针指向的节点,当然还要修改 first 指针指向新的头节点。除此之外,原来的头节点变成了第二个节点,所以还要修改原来头节点的前驱指针,使它指向表头节点,
源码的实现如下:

private void linkFirst(E e) {
 	final Node<E> f = first;
 	//当前节点的前驱指向 null,后继指针原来的头节点
 	final Node<E> newNode = new Node<>(null, e, f);
 	//头指针指向新的头节点
 	first = newNode;
	 //如果原来有头节点,则更新原来节点的前驱指针,否则更新尾指针
 	if (f == null)
 		last = newNode;
 	else
	    f.prev = newNode;
 		size++;
		modCount++;
}

向表尾插入元素
Java8——LinkedList源码详解_第4张图片
当向表尾插入一个节点时,很显然当前节点的后继一定为 null,而前驱结点是 last 指针指向的节点,然后还要修改 last 指针指向新的尾节点。此外,还要修改原来尾节点的后继指针,使它指向新的尾节点
源码的实现如下:

void linkLast(E e) {
 	final Node<E> l = last;
 	//当前节点的前驱指向尾节点,后继指向 null
	 final Node<E> newNode = new Node<>(l, e, null);
	 //尾指针指向新的尾节点
	 last = newNode;
 	//如果原来有尾节点,则更新原来节点的后继指针,否则更新头	指针
	 if (l == null)
		 first = newNode;
	 else
 		l.next = newNode;
 		size++;
	    modCount++;
}

在指定节点之前插入
Java8——LinkedList源码详解_第5张图片
当向指定节点之前插入一个节点时,当前节点的后继为指定节点,而前驱结点为指定节点的前驱节点。此外,还要修改前驱节点的后继为当前节点,以及后继节点的前驱为当前节点,源码的实现如下:

void linkBefore(E e, Node<E> succ) {
	 // assert succ != null;
 	//指定节点的前驱
 	final Node<E> pred = succ.prev;
 	//当前节点的前驱为指点节点的前驱,后继为指定的节点
 	final Node<E> newNode = new Node<>(pred, e, succ);
 	//更新指定节点的前驱为当前节点
 	succ.prev = newNode;
 	//更新前驱节点的后继
 	if (pred == null)
 		first = newNode;
 	else
 		pred.next = newNode;
 		size++;
 		modCount++;
}

删除元素

需要把当前节点的前驱节点的后继修改为当前节点的后继,以及当前节点的后继结点的前驱修改为当前节点的前驱
Java8——LinkedList源码详解_第6张图片

//删除表头节点,返回表头元素的值
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; //新头节点的前驱为 null
 	     size--;
 	     modCount++;
 	     return element;
}
//删除表尾节点,返回表尾元素的值
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; //新尾节点的后继为 null
 		size--;
 		modCount++;
 		return element;
}
//删除指定节点,返回指定元素的值
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;
 		size--;
 		modCount++;
 		return element;
}

其他方法

//删除表头元素
public E removeFirst() {
 	final Node<E> f = first;
 	if (f == null)
 		throw new NoSuchElementException();
 	return unlinkFirst(f);
}
//删除表尾元素
public E removeLast() {
 	final Node<E> l = last;
 	if (l == null)
 		throw new NoSuchElementException();
  	return unlinkLast(l);
}
//插入新的表头节点
public void addFirst(E e) {
 	linkFirst(e);
}
//插入新的表尾节点
public void addLast(E e) {
 	linkLast(e);
}
//链表的大小
public int size() {
 	return size;
}
//添加元素到表尾
public boolean add(E e) {
 	linkLast(e);
 	return true; 
 }
//删除指定元素
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; 
}
//获取指定下标的元素
public E get(int index) {
 	checkElementIndex(index); //先检查是否越界
 	return node(index).item;
}
//替换指定下标的值
public E set(int index, E element) {
 	checkElementIndex(index);
 	Node<E> x = node(index);
 	E oldVal = x.item;
 	x.item = element;
 	return oldVal;
}
//在指定位置插入节点
public void add(int index, E element) {
 	checkPositionIndex(index);
 	if (index == size)
 		linkLast(element);
 	else
 		linkBefore(element, node(index));
}
//删除指定下标的节点
public E remove(int index) {
 	checkElementIndex(index);
 	return unlink(node(index));
}
//获取表头节点的值,表头为空返回 null
public E peek() {
 	final Node<E> f = first;
 	return (f == null) ? null : f.item;
}
//获取表头节点的值,表头为空抛出异常
public E element() {
 	return getFirst();
}
//获取表头节点的值,并删除表头节点,表头为空返回 null
public E poll() {
 	final Node<E> f = first;
 	return (f == null) ? null : unlinkFirst(f);
}
//添加元素到表头
public void push(E e) {
 	addFirst(e);
}
//删除表头元素
public E pop() {
 	return removeFirst();
}

总结

1、LinkedList的底层结构是一个带头/尾指针的双向链表,可以快速的对头/尾节点进行操作。
2、相比数组,链表的特点就是在指定的位置插入和删除元素的效率比较高,但是查找的效率不如数组高。

你可能感兴趣的:(Java8——LinkedList源码详解)