LinkedList原理及源码解析


声明:本文转载自https://my.oschina.net/mvilplss/blog/1154517,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

简介

LinkedList是一个双向线性链表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

image

UML关系图

image

使用示例

LinkedList list = new LinkedList<>(); //新增 list.add("a"); list.add("a"); list.add("b"); System.out.println(list);  //删除 list.remove("a");//删除第一个对应对象 list.remove(0);//删除下标为0的元素 list.removeFirst();//删除第一个元素 list.removeLast();//删除最后一个元素 System.out.println(list);  //修改 list.set(0,"c");//修改下标为0的元素 System.out.println(list);  //插入  list.add(1,"a");//只能在已有元素的前后或中间位置插入 System.out.println(list);  //获取 list.get(0);//根据下标获取元素 list.getFirst();//获取第一个元素 list.getLast();//获取最后一个元素  //循环 //普通循环 for(int i=0;i<list.size();i++){     System.out.println(list.get(i)); } //foreach循环 for(Object o:list){     System.out.println(o); } //迭代循环 Iterator iterator = list.iterator(); while (iterator.hasNext()){     System.out.println(iterator.next()); }

源码解析

初始化

节点

//链表的核心类-节点 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;     } }

构造方法

//默认大小 transient int size = 0; //第一个节点 transient Node<E> first; //最后一个节点 transient Node<E> last; //无参构造 public LinkedList() { } //带有初始化集合构造 public LinkedList(Collection<? extends E> c) {     this();     addAll(c); }

addAll的分析

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;     //如果index==size,说明链表只有一个节点     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;     }          //当一个节点的时候last和当前的节点相同     if (succ == null) {         last = pred;     } else {         pred.next = succ;         succ.prev = pred;     }          //更新链表的长度     size += numNew;     modCount++;     return true; }

常用函数分析

增加

//增加元素,默认向链表的结尾增加节点 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 = newNode;     //如果原来尾部节点为空,说明为空链表需要设置链表头部节点     //否则将原来链表尾部节点的next指向新的节点     if (l == null)         first = newNode;     else         l.next = newNode;     //增加大小     size++;     //记录修改次数,主要用于集合迭代,保证迭代数据的准确性     //在迭代时候如果集合发生变化则会抛出异常     //throw new ConcurrentModificationException();     modCount++; }

删除

//根据下标进行删除节点 public E remove(int index) {     //检测下标是否越界     checkElementIndex(index);     return unlink(node(index)); } //获取index的节点 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 removeFirst() {         final Node<E> f = first;         if (f == null)             throw new NoSuchElementException();         return unlinkFirst(f);     } //断开第一个节点 private E unlinkFirst(Node<E> f) {     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; }  //删除最后一个节点 public E removeLast() {     final Node<E> l = last;     if (l == null)         throw new NoSuchElementException();     return unlinkLast(l); } //断开最后一个节点 private E unlinkLast(Node<E> l) {     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; }

修改(替换)元素

//修改某个元素 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);     //判断下标是否为最后一个位置,如果是直接插入到最后一个节点     //否则查询出当前index的节点,将新的节点放到原来index节点之前     if (index == size)         linkLast(element);     else         linkBefore(element, node(index)); } //插入index节点之前位置 void linkBefore(E e, Node<E> succ) {     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++; }

获取元素

//根据下标获取元素 public E get(int index) {     //检测下标是否越界     checkElementIndex(index);     //根据下标获取节点,然后返回节点的元素     return node(index).item; }  //获取第一个元素 public E getFirst() {     final Node<E> f = first;     if (f == null)         throw new NoSuchElementException();     return f.item; }  //获取最后一个元素 public E getLast() {     final Node<E> l = last;     if (l == null)         throw new NoSuchElementException();     return l.item; }

清除所有数据

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     //将所有数据进行逐个赋值为null,帮助gc的垃圾回收     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++; }

总结

  • 优点
    1、插入和移除数据效率高 2、链表形式方便做为栈或队列场景使用 3、相对arraylist来说,linkedlist无需扩容
  • 缺点
    1、根据随机获取元素效率低(根据下标获取)
    2、批量添加集合效率低

其他

在arraylist和linkedlist中的全局标量都出现了一个修饰符transient,此修饰符的属性默认是不能够被序列化的,jdk作者为了减少空间的浪费所以实现了writeObject(ObjectOutputStream)和readObject(ObjectInputStream)这两个方法,进行手动的序列化有存储数据的有用属性。

参考

更多请关注微信………

本文发表于2017年07月04日 20:36
(c)注:本文转载自https://my.oschina.net/mvilplss/blog/1154517,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1972 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1