Java集合(五)--LinkedList源码解读

首先看一下LinkedList基本源码,基于jdk1.8

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() {
    }

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
  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基本代码结构,可以看出来LinkedList本质上链表

链表一般分为:单向链表、单向循环链表、双向链表、双向循环链表

LinkedList就是一个双向链表,而且实现了Deque,也可以当做双端队列使用,使用方法比较丰富

PS:JDK1.6的LinkedList为双向循环链表,之后去掉header,通过双向链表实现

一、添加:

offer()、add()和linkLast():

public boolean add(E e) {
    linkLast(e);  //添加到尾部
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;  //最后一个节点
    final Node<E> newNode = new Node<>(l, e, null);  //生成一个新节点,前置为last,数据为e,next为null
    last = newNode;  //将新节点赋值为last
    if (l == null)  //如果l为null,意味着链表为空,所以置为首节点
        first = newNode;
    else
        l.next = newNode;  //否则将新节点置为l的next节点
    size++;  
    modCount++;  //修改次数modCount+1
}

 

addAll():

public boolean addAll(int index, Collection<? extends E> c) {
	checkPositionIndex(index);	//检查下标位置是否越界

	Object[] a = c.toArray();	//将Collection转换为数组
	int numNew = a.length;		//数组的长度numNew
	if (numNew == 0)
		return false;

	Node<E> pred, succ;		//定义两个节点pred,succ。pred为插入集合的前一个节点,succ为插入集合的后一个节点
	if (index == size) {	//如果插入的位置等于size,将集合元素加到链表的尾部
		succ = null;	//succ赋值为null
		pred = last;	//pred赋值为last
	} else {
		succ = node(index);	//succ赋值为index节点
		pred = succ.prev;	//succ的prev指向pred
	}

	for (Object o : a) {	//遍历数组
		@SuppressWarnings("unchecked") E e = (E) o;	//元素转换为E
		Node<E> newNode = new Node<>(pred, e, null);	//生成一个新节点,并且把prev指向pred
		if (pred == null)	//如果pred为null,newNode为首节点
			first = newNode;
		else
			pred.next = newNode;	//pred的next指向newNode
		pred = newNode;		//把newNode赋值为pred
	}

	if (succ == null) {	//如果插入到尾部
		last = pred;	pred赋值为last,pred此时为数组中最后一个元素Node
	} else {
		pred.next = succ;	//pred的next指向succ
		succ.prev = pred;	//succ的prev指向pred
	}

	size += numNew;	//重新赋值size
	modCount++;		//修改次数增加一次
	return true;
}

图中把每一步都表现出来了,不可能看不懂吧

PS:

  把Collection转换为数组的目的:toArray()保证传进来的这个集合不会被任何地方引用,也保证这个集合不会有任何机会被修改,保证了数

据的安全性

offFirst()、offLast()、addLast()和addFirst()这里就不讲了,看了上面,这里就很容易理解

二、删除:

poll()、remove()、removeFirst()和unlinkFirst():

public E remove() {
	return removeFirst();
}
public E removeFirst() {	//删除first
	final Node<E> f = first;	//得到first节点
	if (f == null)
		throw new NoSuchElementException();
	return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
	// assert f == first && f != null;
	final E element = f.item;		//得到first节点的item
	final Node<E> next = f.next;	//first节点的next
	f.item = null;	//item置为null
	f.next = null; //first节点的next置为null
	first = next;	//将first节点的next置为首节点
	if (next == null)
		last = null;
	else
		next.prev = null;
	size--;
	modCount++;
	return element;
}

pollLast()、removeLast()和unlinkLast():

public E removeLast() {
	final Node<E> l = last;	//得到last节点
	if (l == null)
		throw new NoSuchElementException();
	return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {	//释放last节点
	// assert l == last && l != null;
	final E element = l.item;
	final Node<E> prev = l.prev;
	l.item = null;
	l.prev = null; 
	last = prev;
	if (prev == null)
		first = null;
	else
		prev.next = null;
	size--;
	modCount++;
	return element;
}

比较简单,看下就明白了

remove(Object)和remove(index):

public boolean remove(Object o) {
	if (o == null) {	//如果o为null
		for (Node<E> x = first; x != null; x = x.next) {
			if (x.item == null) {	//遍历node直到找到第一个null的index
				unlink(x);	//删除index节点
				return true;
			}
		}
	} else {
		for (Node<E> x = first; x != null; x = x.next) {
			if (o.equals(x.item)) {//遍历node直到找到第一个o的index
				unlink(x);
				return true;
			}
		}
	}
	return false;
}
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) {	//如果为上个节点prev等于null,直接把下个节点指向first
		first = next;
	} else {	
		prev.next = next;	//prev的next赋值为下个node
		x.prev = null;	当前节点的上个节点置为null
	}

	if (next == null) {		//如果删除last节点,直接把上个节点置为last节点
		last = prev;
	} else {
		next.prev = prev;	//next节点的prev指向prev节点
		x.next = null;	//当前节点的next置为null
	}

	x.item = null;	//当前节点item置为null,size--
	size--;
	modCount++;
	return element;
} 

三、修改:set(index, element):

public E set(int index, E element) {
	checkElementIndex(index);	//检查index是否越界
	Node<E> x = node(index);	//获取index对应的节点
	E oldVal = x.item;	//获取节点的item
	x.item = element;	//重新赋值节点的item
	return oldVal;	//返回oldVal
}

四、获取:

get(index)和node(index):

public E get(int index) {
	checkElementIndex(index);	//检查下标
	return node(index).item;	//返回index对应node的item
}
Node<E> node(int index) {	//使用二分法查找
	if (index < (size >> 1)) {	如果index小于size的一半
		Node<E> x = first;	//获取first节点
		for (int i = 0; i < index; i++)	//因为链表不能随机访问,所以只能从0遍历到index,最终返回index对应的node
			x = x.next;
		return x;
	} else {	
		Node<E> x = last;	//获取last节点
		for (int i = size - 1; i > index; i--)	//从size-1遍历到index,最终返回index对应的node
			x = x.prev;
		return x;
	}
}

getFirst():

public E getFirst() {
	final Node<E> f = first;
	if (f == null)
		throw new NoSuchElementException();
	return f.item;
}

getLast():

public E getLast() {
	final Node<E> l = last;
	if (l == null)
		throw new NoSuchElementException();
	return l.item;
}

contains(Object)和indexOf(Object):

public int indexOf(Object o) {
	int index = 0;
	if (o == null) {	//如果Object为null
		for (Node<E> x = first; x != null; x = x.next) {	//遍历得到第一个null,返回index
			if (x.item == null)
				return index;
			index++;
		}
	} else {
		for (Node<E> x = first; x != null; x = x.next) {	//遍历得到第一个和object相等的node.item,返回index
			if (o.equals(x.item))
				return index;
			index++;
		}
	}
	return -1;	//如果没有,返回-1
}

for循环效率:for、foreach、lambda表达式的foreach、Iterator

测试:

public static void main(String[] args) throws IOException {
	LinkedList<String> list = new LinkedList<>();
	list.add("abc");
	list.add("def");
	for (int i = 0; i < 10000; i++) {
		list.add("abc");
	}
	long startTime = System.currentTimeMillis();
	for (int i = 0; i < list.size(); i++) {
		System.out.print(list.get(i) + " ");
	}
	System.out.println(" ");
	System.out.println("普通for循环花费时间:" + (System.currentTimeMillis() - startTime));
	long startTime1 = System.currentTimeMillis();
	for (String s : list) {
		System.out.print(s + " ");
	}
	System.out.println(" ");
	System.out.println("foreach循环花费时间:" + (System.currentTimeMillis() - startTime1));
	long startTime3 = System.currentTimeMillis();
	list.forEach(s -> {
		System.out.print(s + " ");
	});
	System.out.println(" ");
	System.out.println("lambda表达式foreach循环花费时间:" + (System.currentTimeMillis() - startTime3));
	long startTime2 = System.currentTimeMillis();
	Iterator iterator = list.iterator();
	while (iterator.hasNext()) {
		String s = (String)iterator.next();
		System.out.print(s + " ");
	}
	System.out.println(" ");
	System.out.println("Iterator循环花费时间:" + (System.currentTimeMillis() - startTime2));
}

为了结果的可信度,我们得到三次输出结果:

普通for循环花费时间:105
foreach循环花费时间:43
lambda表达式foreach循环花费时间:78
Iterator循环花费时间:31

普通for循环花费时间:97
foreach循环花费时间:47
lambda表达式foreach循环花费时间:79
Iterator循环花费时间:32

普通for循环花费时间:83
foreach循环花费时间:49
lambda表达式foreach循环花费时间:77
Iterator循环花费时间:34

普通for循环和lambda表达式foreach都很慢,Iterator最快

普通for循环最慢,应该是可以想象到的,因为LinkedList不能随机访问,每次获取都要从头到尾遍历,我们遍历10000次

虽然使用二分法可以提高效率,靠近中间的index,效率真的很慢,例如4999,5000,5001

而Iterator通过ListItr实现:

private class ListItr implements ListIterator<E> {
	private Node<E> lastReturned;	//本次返回的node
	private Node<E> next;	//本次返回的node的next节点
	private int nextIndex;	//下一个node对应的index
	private int expectedModCount = modCount;

	ListItr(int 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;
	}
	
}

  每次都会记录这次返回的node,下次遍历,直接取node.next,例如第4999次遍历的时候,直接返回element4998.next,而不需要像普通for循环

一样,先得到1,再是2,然后3,最后到4999

到这里,对LinkedList的了解已经差不多零,能得到的内容:

1、LinkedList由双向链表实现,add(index,element)的效率很高,只需要直接修改node的prev和next关系,但是需要new node

2、删除的时候也很快

3、不涉及到初始容量、加载因子、扩容等概念

4、不能随机访问,查询效率较慢相对于ArrayList差很多

总结:对链表的任意修改都可以归结:改变node的前后指向关系

posted @ 2019-06-20 15:57  Diamond-Shine  阅读(320)  评论(0编辑  收藏  举报