JAVA集合框架6---LinkedList源码解析

上一篇中,我们分析了ArrayList的源码,我们知道ArrayList底层是使用数组来组织数据的,接下来我们将继续分析List接口的另外一个实现类:LinkedList。与ArrayList不同之处,LinkedList底层使用双向链表来组织数据的,每个结点有三个域,分别为:储存数据的item域、指向下一个结点的next域以及指向前一个结点的prev域,下面是底层结点的源码:

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的构造方法:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    // 集合大小
    transient int size = 0;
    // 指向第一个结点
    transient Node<E> first;
    // 指向尾结点
    transient Node<E> last;
    
    // 无参构造函数
    public LinkedList() {
    }
    // 使用另外一个集合来初始化LinkedList
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
}

从上述源码中我们可以发现,LinkedList继承自 AbstractSequentialList,AbstractSequentialList是AbstractList的子类,它重写了AbstractList中的一些方法,使得更快的实现顺序集合,比如LinkedList。下面是AbstractSequentialList的源码:

public abstract class AbstractSequentialList<E> extends AbstractList<E> {
    protected AbstractSequentialList() {
    }

    public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
    public E set(int index, E element) {
        try {
            ListIterator<E> e = listIterator(index);
            E oldVal = e.next();
            e.set(element);
            return oldVal;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
    public void add(int index, E element) {
        try {
            listIterator(index).add(element);
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
    public E remove(int index) {
        try {
            ListIterator<E> e = listIterator(index);
            E outCast = e.next();
            e.remove();
            return outCast;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        try {
            boolean modified = false;
            ListIterator<E> e1 = listIterator(index);
            for (E e : c) {
                e1.add(e);
                modified = true;
            }
            return modified;
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }
    public Iterator<E> iterator() {
        return listIterator();
    }
    public abstract ListIterator<E> listIterator(int index);
}

这些方法都比较简单,都是通过List迭代器来操作的,因此子类只需要重写listIterator方法即可完成一个简单的顺序List了。

另外,LinkedList除了实现了List接口还实现了Deque接口,Deque接口规定了一个双端队列的规范,还包含的栈的push与pop操作,故Deque也可以当做一个栈来使用。此外Deque接口继承自Queue接口,Queue是一个先进先出的队列接口。

因此LinkedList还可以当做双端队列(Deque<E>  deque = new LinkedList<>())、栈(Deque<E> stack = new LinkedList<>())以及队列   (Queue<E> queue = new LinkedList<>())来使用,它们的底层都是使用双向链表来实现的。Java还提供了底层基于数组的Deque实现类ArrayDeque。关于ArrayDeque的源码解析我们稍后在来解析。现在先看看Queue以及Deque接口提供了那些方法。

Queue接口源码如下:

public interface Queue<E> extends Collection<E> {
    boolean add(E e);
    boolean offer(E e);
    E remove();
    E poll();
    E element();
    E peek();
}

Queue扩展了Collection接口,它的主要操作有三种:

1、在尾部添加元素(add,offer)。

2、查看头部元素(element,peek).返回头部元素,但不改变队列。

3、删除头部元素(remove,poll),返回头部元素,并且从队列中删除。

可以看到每一种操作都有两种形式,区别在于对特殊情况的处理不同。特殊情况指的是队列为空或者队列为满,为空容易理解,为满指的是队列大小受到限制而且已经占满了。在LinkedList的实现中,队里的长度没有限制,但是别的Queue的实现可能有。在队列为空时,element和remove会抛出NoSuchElementException异常,而peek和poll返回特殊值null。在队列为满时,add会抛出IllegalArgumentException异常,而offer知识返回false(JDK 7,别的版本可能不一样,JDk8也是抛出异常)。

双端队列Deque扩展了队列Queue,队列Queue只能在队头删除元素,队尾插入元素,而双端队列Deque在两头都可以插入、删除元素。Deque扩展Queue的源码如下:

public interface Deque<E> extends Queue<E> {
    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);

    // 栈的方法
    void push(E e);
    E pop();
    E peek();

    // 迭代器
    Iterator<E> iterator();
    Iterator<E> descendingIterator();
}

操作的含义与方法名是一样的,同样每种操作的提供了两个版本,区别与Queue中的一样。

明白了LinkedList的继承体系之后我们再来看LinkedList底层的实现原理。在介绍LinkedList的构造方法时我们给出了LinkedList中最主要的三个成员变量:size、first与last。LinkedList中所有的public方法都是围绕着这三个变量来展开的。

先看几个重要的私有方法:

// 添加对象e到链表的头部
private void linkFirst(E e) {
	final Node<E> f = first; 
	final Node<E> newNode = new Node<>(null, e, f);
	first = newNode;  // 更新头结点
	if (f == null)  // 如果加入结点时,原链表为空,则同时也更新尾指针last,指向newNode
		last = newNode;
	else
		f.prev = newNode;
	size++;
	modCount++;
}
// 添加对象e到链表的尾部
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++;
}
// 添加对象e到结点succ的前面
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++;
}
// 删除头结点(传入的参数f就是头结点)
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;
}
// 删除尾结点(传入的参数l就是尾结点)
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;
}
// 删除结点x
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;
}
// 更具索引值返回对应的结点
Node<E> node(int index) {
	// assert isElementIndex(index);

	if (index < (size >> 1)) {  // 如果索引值小于 size/2 从前往后找
		Node<E> x = first;
		for (int i = 0; i < index; i++)
			x = x.next;
		return x;
	} else {  // 索引值大于 size/2 从后往前找
		Node<E> x = last;
		for (int i = size - 1; i > index; i--)
			x = x.prev;
		return x;
	}
}

这些都是双向链表的基本操作,如果觉得困难的话先去补一补双向链表的知识吧。

// 返回首元素,为空时抛出异常 继承自Deque
public E getFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return f.item;
}
// 返回末尾元素,为空时抛出异常, 继承自Deque
public E getLast() {
	final Node<E> l = last;
	if (l == null)
		throw new NoSuchElementException();
	return l.item;
}
// 删除首元素 ,为空时抛出异常,继承自Deque
public E removeFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return unlinkFirst(f);
}
// 删除末尾元素,为空时抛出异常,继承自Deque
public E removeLast() {
	final Node<E> l = last;
	if (l == null)
		throw new NoSuchElementException();
	return unlinkLast(l);
}
// 在标头插入元素 , 继承自 Deque
public void addFirst(E e) {
	linkFirst(e);
}
// 在表尾插入元素, 继承自Deque
public void addLast(E e) {
	linkLast(e);
}
// 在表尾插入元素
public boolean add(E e) {
	linkLast(e);
	return true;
}
// 在对应的索引处插入元素
public void add(int index, E element) {
	checkPositionIndex(index); // 检测插入位置 index 是否合法

	if (index == size)
		linkLast(element);
	else
		linkBefore(element, node(index));
}
private void checkPositionIndex(int index) {
	if (!isPositionIndex(index))
		throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
	return index >= 0 && index <= size;
}
// 返回表的长度
public int size() {
	return size;
}
// 根据值来删除元素
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 remove(int index) {
	checkElementIndex(index);
	return unlink(node(index));  // 先获取对应索引的对象,然后再删除
}
// 是否包含元素
public boolean contains(Object o) {
	return indexOf(o) >= 0;
}
// 返回元素对应的索引
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;
}
// 根据索引返回对应值
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 boolean addAll(Collection<? extends E> c) {
	return addAll(size, c);
}
//  在指定位置增加
public boolean addAll(int index, Collection<? extends E> c) {
	checkPositionIndex(index);

	Object[] a = c.toArray();
	int numNew = a.length;
	if (numNew == 0)
		return 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;
}
// 清除结点
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++;
}

总体来说,LinkedList的源码还是比较简单的,基本都是一些简单的链表操作,上面并没有列出LinkedList的所有操作,其余的操作也都比较简单,就不浪费篇幅了。

猜你喜欢

转载自blog.csdn.net/qq_22158743/article/details/87776194
今日推荐