Java中集合类源码分析(4)-----LinkedList源码分析

本文通过JDK1.8版本LinkedList源码分(fan)析(yi)介绍LinkedList的底层实现方式和主要API,并介绍其与ArrayList的差异.
本文是该系列文的第四篇.
##1.顶部注释和LinkedList定义

/**
 *双链列表是List和Deque接口的实现.其实现了所有list的可选择性操作,并且允许所有的元素(包括null,空元素)
 *
 *所有的操作是最大限度被期望成一个双链表形式的.在list中操作索引将从头或者尾在list中穿梭,这取决于哪一边跟靠近指定索引.
 *
 *注意LinkedList不是同步的,其线程是不安全的.当有多个线程同时操作LinkedList时,有可能会导致数据错误,
 *所以如果需要多线程共享LinkedList时,最好使用synchronizedList来初始化:
 *				List list = Collections.synchronizedList(new LinkedList(...));
 *
 *大意为Collections.synchronizedList方法可以实现线程安全的操作。
 *
 *大意为由iterator()和listIterator()返回的迭代器是fail-fast的
 */

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
  • LinkedList:说明它支持泛型。
  • extends AbstractSequentialList
    AbstractSequentialList 继承自AbstractList,但AbstractSequentialList
  • 只支持按次序访问,而不像 AbstractList 那样支持随机访问。这是LinkedList随机访问效率低的原因之一。
  • implements List:说明它支持集合的一般操作。
  • implements Deque:Deque,Double ended queue,双端队列。LinkedList可用作队列或双端队列就是因为实现了它。

为什么使用LinkedList?它的底层是如何保存数据的?
ArrayList的底层是数组实现,如果只在数组的高端执行插入(使用add),那么ArrayList非常适用.不过,如果不是在高端进行插入,那么ArrayList是一个极差的选择,因为我们必须移动很多项.
在LinkedList中,我们不是以常见的连续数组,而是以不连续的方式来存储项.为了做到这一点,我们将每个对象存储在一个节点中(node),节点包含对象和对表中下一节点的应用.在这种情况下,我们要维护表中对第一个节点和最后一个节点的引用.看看源码如何实现.

    private static class Node<E> {//节点类,一个内部类
        E item;//E类型的数据(域)
        Node<E> next;//保存着下(后)一个的引用
        Node<E> prev;//保存着上(前)一个的引用

        Node(Node<E> prev, E element, Node<E> next) {//node的构造方法,直接传入(上一个节点引用,数据域,下一个节点的引用).
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

##2.全局变量和构造方法

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
	
	//LinkedLis大小,即节点个数
    transient int size = 0;

    /**
     * 指向头节点
     */
    transient Node<E> first;

    /**
     * 指向尾个节点
     */
    transient Node<E> last;

    /**
     * 构造一个空的LinkedList
     */
    public LinkedList() {
    }

    /**
     *构造一个list,其包含着指定集合中的元素,按照此集合迭代器返回的顺序. 
     *
     * @param  c 将放入这个LinkedList的元素们所在的集合.
     * @throws NullPointerException 如果指定的集合为空.
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

##3 操作LinkedList底层Node的方法
底层操作包含2类(对节点的添加,删除)6个.

	/**
	 * 链接e作为第一个元素(头结点)
	 */
	private void linkFirst(E e) {
		// 使一个新的节点f指向原来的头结点first.
		final Node<E> f = first;
		/*
		 * 创建新的节点newNode,使新的节点的往前指的上一个节点引用为空 (即没有上一个节点,因为自己就是头结点),下一个节点的引用是原来的first
		 */
		final Node<E> newNode = new Node<>(null, e, f);
		// 现在头指针不再指向原来的头结点,而指向newNode
		first = newNode;
		// 如果列表本来为空
		if (f == null)
			last = newNode;// 则尾元素也是新建的元素
		else
			// 原来的第一个节点(现在的第二个)头部指向新建的头结点
			f.prev = newNode;
		size++;//
		modCount++;
	}

	/**
	 * 连接e作为其尾节点
	 */
	void linkLast(E e) {
		// 将l指向原来的尾节点
		final Node<E> l = last;
		// 将newNode指向新的尾节点,这个新的尾节点向前指向last(即原来的尾节点),向后没有.
		final Node<E> newNode = new Node<>(l, e, null);
		// 原来的尾节点的引用指向了newNode
		last = newNode;
		if (l == null)
			first = newNode;// 如果LinkedList为空的话,那头结点是它啦
		else
			// 当然,还需要node内部实现:原来尾节点的向后指针指向newNode
			l.next = newNode;
		size++;
		modCount++;
	}

	/**
	 * 在指定的节点succ前插入一个元素e
	 */
	void linkBefore(E e, Node<E> succ) {
		// 假设succ不为null
		final Node<E> pred = succ.prev;//得到succ的前一个节点引用.
		//构造一个新节点newNode,其向前的指针指向succ的前一个节点引用,向后的指向succ
		final Node<E> newNode = new Node<>(pred, e, succ);
		//现在succ的向前指针也指向了newNode了
		succ.prev = newNode;
		if (pred == null)//如果succ是头结点的话
			first = newNode;
		else
			pred.next = newNode;//原来succ的前一个节点引用  的向后的指针将指向newNode
		size++;
		modCount++;
	}

	/**
	 * 删除头结点元素f,假设其不为null
	 */
	private E unlinkFirst(Node<E> f) {
		// 假设 f == first && f != null;
		final E element = f.item;//先把数据获取出来,用element指向
		final Node<E> next = f.next;//获取头节点后面一个节点
		f.item = null;//使头结点上的数据为空
		f.next = null; // help GC
		first = next;//原本的头结点的引用指向了next
		if (next == null)//如果next也为空
			last = null;//则尾节点也是空,说明移出后这个list里没元素了
		else
			next.prev = null;//next的向前指向也为空了,因为它自己就是头结点
		size--;
		modCount++;
		return element;//返回删除的节点的数据
	}

	/**
	 * 移除尾节点l,假设其不为null
	 */
	private E unlinkLast(Node<E> l) {
		// 假设 l == last && l != null;
		final E element = l.item;//还是先把l的数据取出来,用引用element指向,等会返回
		final Node<E> prev = l.prev;//获取l的前一个节点,用prev指向
		l.item = null;//将l的数据设置为null
		l.prev = null; // help GC
		last = prev;//原来尾节点的引用指向了prev
		if (prev == null)//如果prev为空
			first = null;//则头结点也为空,说明移出后这个list里没有元素了
		else
			prev.next = null;//现在prev的下一个节点引用为空了
		size--;
		modCount++;
		return element;
	}

	/**
	 * 移除指定的不为空的节点
	 */
	E unlink(Node<E> x) {
		// 假设 x != null;
		final E element = x.item;//用element保存x的数据
		final Node<E> next = x.next;//next指向x的后一个节点
		final Node<E> prev = x.prev;//prev指向x的前一个节点

		if (prev == null) {//prev为空,即x是头结点
			first = next;//x的后一个节点将成为list的头结点
		} else {
			prev.next = next;//x的前一个节点引用的向后指针将指向x的下一个,即将x跳过了
			x.prev = null;
		}

		if (next == null) {//next为空,即x是尾节点
			last = prev;
		} else {
			next.prev = prev;//x的后一个节点的向前指向的指针将指向x的前一个节点,巧妙地避开了x
			x.next = null;
		}

		x.item = null;//x的数据也为null,至此,x为null了
		size--;
		modCount++;
		return element;
	}

##4.LinkedList的常用方法
####单个添加,删除,查找

	/**
	 * 返回list中的第一个元素.
	 */
	public E getFirst() {
		final Node<E> f = first;//得到头结点
		if (f == null)
			throw new NoSuchElementException();//如果为空,将报异常
		return f.item;//注意,一般我们获取的是节点中保存的数据
	}

	/**
	 * 返回list中的最后一个元素
	 */
	public E getLast() {
		final Node<E> l = last;//得到尾节点,l来指向
		if (l == null)
			throw new NoSuchElementException();
		return l.item;//得到尾节点中包含的数据
	}

	/**
	 * 移除并返回第一个元素
	 */
	public E removeFirst() {
		final Node<E> f = first;
		if (f == null)
			throw new NoSuchElementException();
		return unlinkFirst(f);//底层调用的是unlinkFirst方法,就可以返回移除的头结点的数据了
	}

	/**
	 * 移除并返回最后一个元素
	 */
	public E removeLast() {
		final Node<E> l = last;
		if (l == null)
			throw new NoSuchElementException();
		return unlinkLast(l);
	}

	/**
	 * 在list头添加指定的元素e
	 */
	public void addFirst(E e) {
		linkFirst(e);//底层调用的是linkFirst(e)方法
	}

	/**
	 * 在表尾添加指定的元素.
	 */
	public void addLast(E e) {
		linkLast(e);
	}

####List通用操作(1)
前面很多已介绍,这里看看实现即可

	public boolean contains(Object o) {
		return indexOf(o) != -1;
	}


	public int size() {
		return size;
	}

	/**
	 *在list尾端继续添加指定元素,添加成功返回true,该效果等同于addLast()方法,底层实现也是一样的
	 */
	public boolean add(E e) {
		linkLast(e);
		return true;
	}

	/**
	 *移除指定元素的在最低索引上的元素,如果其不存在与该list,则该list不变.如果list有指定元素并移除成功,返回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;
	}

	/**
	 *添加指定集合到list的尾端,其顺序即为迭代顺序
	 */
	public boolean addAll(Collection<? extends E> c) {
		return addAll(size, c);//底层就是调用的接下来的方法
	}

	/**
	 *将指定的集合从指定的位置上开始插入到list中.后面的元素全部向后移位,链表发生改变,返回true
	 */
	public boolean addAll(int index, Collection<? extends E> c) {
		checkPositionIndex(index);//检查插入的位置是否合法

		Object[] a = c.toArray();
		int numNew = a.length;
		if (numNew == 0)
			return false;//如果c是空的话那么就返回false
		
		//定义两个节点指针,指向插入点前后的节点元素
		Node<E> pred, succ; 
		if (index == size) {
			succ = null;
			pred = last;
		} else {
			succ = node(index);
			pred = succ.prev;
		}
		
		// 插入集合中所有元素
		for (Object o : a) {
			@SuppressWarnings("unchecked")
			E e = (E) o;
			Node<E> newNode = new Node<>(pred, e, null);
			if (pred == null)
				first = newNode;
			else
				pred.next = newNode;
			pred = newNode;
		}
		
		// 修改插入后的指针问题
		if (succ == null) {
			last = pred;
		} else {
			pred.next = succ;
			succ.prev = pred;
		}

		size += numNew;
		modCount++;
		return true;
	}

	/**
	 * 删除list中的所有元素
	 */
	public void clear() {
		// Clearing all of the links between nodes is "unnecessary", but:
		// - helps a generational GC if the discarded nodes inhabit
		// more than one generation
		// - is sure to free memory even if there is a reachable Iterator
		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++;
	}

####List的通用操作(2)
原文注释的是 “位置获取操作”
先来看一个方法,其为包访问权限.之后很多方法会用到.

	/**
	 * 返回指定索引处的非空元素
	 */
	Node<E> node(int 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;
		}
	}
	/**
	 * 根据索引得到元素
	 */
	public E get(int index) {
		checkElementIndex(index);//检查查找的位置是否合法
		return node(index).item;//node方法
	}

	/**
	 *将指定索引上的元素替换为指定的元素,返回被替换的元素(节点的数据)
	 */
	public E set(int index, E element) {
		checkElementIndex(index);//检查传入的位置参数是否合法
		Node<E> x = node(index);//通过位置获取节点
		E oldVal = x.item;//得到该位置上的节点的数据,用oldVal指向
		x.item = element;//将要传入的元素赋给x节点的数据
		return oldVal;//oldVal
	}

	/**
	 * 在指定位置插入元素e
	 */
	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));
	}

	/**
	 *检查索引是否越界
	 */
	private boolean isElementIndex(int index) {
		return index >= 0 && index < size;
	}

	private boolean isPositionIndex(int index) {
		return index >= 0 && index <= size;
	}

	/**
	 * 创建一个越界信息,在服务器端和客户端虚拟机上都好用?
	 */
	private String outOfBoundsMsg(int index) {
		return "Index: " + index + ", Size: " + size;
	}

	private void checkElementIndex(int index) {
		if (!isElementIndex(index))
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}

	private void checkPositionIndex(int index) {
		if (!isPositionIndex(index))
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}

####List的通用操作(3)
原文注释:查询操作

	/**
	 *返回指定元素的最小索引,若list中没有,则返回-1
	 */
	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 {
			for (Node<E> x = first; x != null; x = x.next) {
				if (o.equals(x.item))
					return index;
				index++;
			}
		}
		return -1;
	}

	/**
	 *返回指定元素的最大索引,若list中没有,则返回-1
	 */
	public int lastIndexOf(Object o) {
		int index = size;
		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;
	}

##5.LinkedList的队列操作和双向队列操作
####(1)队列操作

	/**
	 *返回头结点的元素,但是并不移除它
	 *如果头节点为null,也返回null
	 */
	public E peek() {
		final Node<E> f = first;
		return (f == null) ? null : f.item;
	}

	/**
	 * 获取表头节点的值(第一个元素),头节点为空抛出异常
	 */
	public E element() {
		return getFirst();
	}

	/**
	 *返回并删除头节点,如果该list为空则返回null
	 */
	public E poll() {
		final Node<E> f = first;
		return (f == null) ? null : unlinkFirst(f);
	}

	/**
	 *移除并返回头节点,如果链表为空,抛出NoSuchElementException异常
	 */
	public E remove() {
		return removeFirst();
	}

	/**
	 * 添加指定元素到list尾部
	 */
	public boolean offer(E e) {
		return add(e);
	}

####(2)双向队列操作

	/**
	 * 将指定元素插入队头,插入成功返回true
	 */
	public boolean offerFirst(E e) {
		addFirst(e);
		return true;
	}

	/**
	 * 将指定元素插入队伟,插入成功返回true
	 */
	public boolean offerLast(E e) {
		addLast(e);
		return true;
	}

	/**
	 * 返回,但不是移除,list头元素,若头结点为空,返回空
	 */
	public E peekFirst() {
		final Node<E> f = first;
		return (f == null) ? null : f.item;
	}

	/**
	 * 返回,但不是移除,list尾元素,若头结点为空,返回空
	 */
	public E peekLast() {
		final Node<E> l = last;
		return (l == null) ? null : l.item;
	}

	/**
	 * 移除并且返回list的头元素,若节点为空,则返回null
	 */
	public E pollFirst() {
		final Node<E> f = first;
		return (f == null) ? null : unlinkFirst(f);
	}

	/**
	 *移除并且返回list的尾元素,若节点为空,则返回null
	 */
	public E pollLast() {
		final Node<E> l = last;
		return (l == null) ? null : unlinkLast(l);
	}

	/**
	 *将指定元素压入到栈头.换句话说,插入指定元素到list头
	 * 此方法等价于addFirst(e)
	 */
	public void push(E e) {
		addFirst(e);
	}

	/**
	 * 移除并返回栈顶元素
	 *
	 * 此方法等价于removeFirst()
	 */
	public E pop() {
		return removeFirst();
	}

	/**
	 *正向遍历栈,删除指定对象第一次出现时,索引对应的元素,若有这个元素,则返回true
	 */
	public boolean removeFirstOccurrence(Object o) {
		return remove(o);
	}

	/**
	 * 反向向遍历栈,删除指定对象第一次出现时,索引对应的元素,若有这个元素,则返回true
	 */
	public boolean removeLastOccurrence(Object o) {
		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;
	}

##6.其余操作

	/**
	 * 返回一个这个LinkedList的浅拷贝(这个元素本身并没有被克隆)
	 */
	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;
	}

	/**
	 *  将所有链表元素按顺序存到数组中,并返回这个数组
	 */
	public Object[] toArray() {
		Object[] result = new Object[size];
		int i = 0;
		for (Node<E> x = first; x != null; x = x.next)
			result[i++] = x.item;
		return result;
	}

	/**
     * 将所有list元素按顺序存到数组中。数组元素的类型是给定的类型。如果list元素的类型和数组元素类型不匹配,将新建一个数组来存储链表元素
     * 
     * @throws NullPointerException 如果指定的数组为null
	 */
	@SuppressWarnings("unchecked")
	public <T> T[] toArray(T[] a) {
		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;

		if (a.length > size)
			a[size] = null;

		return a;
	}

##7.关于其内部类ListItr

public ListIterator<E> listIterator(int index) {
		checkPositionIndex(index);
		return new ListItr(index);
	}

	private class ListItr implements ListIterator<E> {
		private Node<E> lastReturned;
		private Node<E> 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 < 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;
		}

		public E previous() {
			checkForComodification();
			if (!hasPrevious())
				throw new NoSuchElementException();

			lastReturned = next = (next == null) ? last : next.prev;
			nextIndex--;
			return lastReturned.item;
		}

		public int nextIndex() {
			return nextIndex;
		}

		public int previousIndex() {
			return nextIndex - 1;
		}

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

		public void set(E e) {
			if (lastReturned == null)
				throw new IllegalStateException();
			checkForComodification();
			lastReturned.item = 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();
		}

		final void checkForComodification() {
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();
		}
	}

	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的序列化,JDK8新特性等并没有将源码贴出,这一部分相对用的较少,之后慢慢加吧.

最最后,我们将ArrayList和LinkedList做一个比较吧

  • ArrayList底层由数组实现,LinkedList底层是双向链表.
  • 二者元素都为有序且可重复.(毕竟实现List接口).
  • ArrayList的查改(随机访问)效率高,增删除效率低.LinkedList随机访问效率低,增删效率高。

感谢大神们的文章,获益匪浅,本文有参考自:
潘威威的博客— Java8源码-LinkedList

发布了47 篇原创文章 · 获赞 108 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Lagrantaylor/article/details/78989100