zoukankan      html  css  js  c++  java
  • 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的前后指向关系

  • 相关阅读:
    CentOS 7 修改时区
    flink与kafka结合
    Kafka 原理和实战
    kafka单机多节点部署
    使用yumdownloadonly下载RPM包及依赖包
    使用Onvif协议进行设备发现以及指定设备信息探测
    kafka相关脚本操作
    Python入门篇-基础语法
    Python入门篇-pyenv安装及应用
    zabbix Server 4.0监控Flume关键参数
  • 原文地址:https://www.cnblogs.com/huigelaile/p/11059257.html
Copyright © 2011-2022 走看看