LlinkedList源码剖析
来源:互联网 发布:淘宝买家怎么贷款啊 编辑:程序博客网 时间:2024/06/05 18:31
LinkedList概述
- LinkedLlist与ArrayList一样实现List接口,知识ArrayList是List接口的大小可变数组的实现。LinkedList是接口表的实现,基于链表实现的方式是的LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList差。
- LinkedList实现所有可选的列表操作,并允许所有的元素包括null。
除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作stack、deque或双端队列。 - 此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
- LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆。
- LinkedList的实现方式决定了所有跟下标相关的操作都是线性时间,而在首段或者末尾删除元素只需要常数时间。为追求效率LinkedList没有实现同步(synchronized),如果需要多个线程并发访问,可以先采用
Collections.synchronizedList()
方法对其进行包装。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
从这段代码中我们可以清晰地看出LinkedList继承AbstractSequentialList,实现List、Deque、Cloneable、Serializable。其中AbstractSequentialList提供了 List 接口的骨干实现,从而最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,从而以减少实现List接口的复杂度。Deque一个线性 collection,支持在两端插入和移除元素,定义了双端队列的操作。
源码
属性
在LinkedList中提供了两个基本属性size、first、last
transient int size = 0; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; transient Node<E> last;
LinkedList通过first和last引用分别指向链表的第一个和最后一个元素。注意这里没有所谓的哑元,当链表为空的时候first和last都指向null
Node节点对象
//Node内部类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; }}
构造方法
/** * 构造一个空列表。 */ public LinkedList() { } /** * 构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。 */ public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
this():首先调用LinkedList(),构造一个空列表将Collection中的所有元素添加到列表中。
add
Java1.6的addAll
addAll的源码
// **将“集合(c)”添加到LinkedList中。实际上,是从双向链表的末尾开始,将“集合(c)”添加到双向链表中**public boolean addAll(Collection<? extends E> c){ return addAll(size, c);}//将指定collection中的所有元素从指定位置开始插入此列表。其中index标识在其中插入指定collection的第一个元素的索引public boolean addAll(int index,Collection<? extends E> c){ //若插入的位置小于0或者大于链表长度,则抛出IndexOutOfBoundsException异常 if(index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); Object[] a = c.toArray(); int numNew = a.length; //若插入的元素为空,则返回false if (numNew == 0) return false; //modCount:在AbstractList中定义的,表示从结构上修改列表的次数 modCount++; //获取插入位置的节点,若插入的位置在size头则是头结点,否则获取index位置处的节点 Entry<E> successor = (index == size? header : entry(index)) //插入位置的前一个节点,在插入过程中需要修改该节点的next引用:指向插入的节点元素 Entry<E> prodecessor = successor.previous; //执行插入动作 for(int i = 0; i < numNew; i++){ //a[i]元素节点,successor下一个节点,predecessor上一个节点 Entry<E> e = new Entry<E>((E)a[i], successor, predecessor); //将插入位置前一个节点和下一个元素引用指向当前元素 predecessor.next = e; //修改插入位置的前一个节点,这样的做的目的是插入位置右移以一位 predecessor = e; }sucessor.prevous = predecessor;}//修改容量大小size += numNew;return true;
在addAll()方法中,涉及了两个方法,一个是entry(int index),该方法为LinkedList的私有方法。该方法为LinkedList的私有方法,主要是用来查找index位置的节点元素。
/** * 返回指定位置(若存在)的节点元素 */ private Entry<E> entry(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); //头部节点 Entry<E> e = header; //判断遍历的方向 if (index < (size >> 1)) { for (int i = 0; i <= index; i++) e = e.next; } else { for (int i = size; i > index; i--) e = e.previous; } return e; }
从该方法有两个遍历方向中我们也可以看出LinkedList是双向链表,这也是在构造方法中为什么需要将header的前、后节点均指向自己。
java1.7的add
这里只说一下add(int index,E element)
1、 现根据index找到要插入的位置
2、 修改引用,完成插入操作
public void add(int index, E element){ checkPositionIndex(index); if(index == size) add(element); else{ Node<E> succ = node(index); //修改引用 final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred,e,succ); succ.prev = newNode; if(pred == null)//插入位置为0 first = newNode; else pred.next = newNode; size ++; }} Node<E> node(int index) { // assert isElementIndex(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; } }
remove
1、先找到要删除元素的引用
2、修改相关引用,完成删除操作。
在寻找被删元素引用的时候remove(Object o)调用的是元素的equals方法,而remove(int index)使用的是下标计数,两种方式都是线性时间复杂度。在
E unlink(Node<E> x){ final E element = x.item; final Node<E> next = x.next; final Node<E> prec = 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--; renturn element;}
总结:
1. 在查找和删除某元素时,源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。
2. LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容
3. 注意源码中的Entry entry(int index)方法。该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index
- LlinkedList源码剖析
- 《stl源码剖析》剖析
- 【源码】ArrayList源码剖析
- 【源码】LinkedList源码剖析
- 【源码】HashMap源码剖析
- 【源码】HashMap源码剖析
- 【源码】Hashtable源码剖析
- 【源码】LinkedHashMap源码剖析
- 【源码】LruCache源码剖析
- 【源码】TreeMap源码剖析
- 【源码】LinkedHashMap源码剖析
- 【源码】LruCache源码剖析
- 【源码】Hashtable源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- 【Java集合源码剖析】LinkedList源码剖析
- 【Java集合源码剖析】Vector源码剖析
- 【Java集合源码剖析】HashMap源码剖析
- 【Java集合源码剖析】ArrayList源码剖析
- Spring学习笔记(二)-----IOC之XML
- 数据结构——串的定义与基本操作
- Python的一些基础知识(一)
- 使用instrument进行性能优化
- 题目39-水仙花数
- LlinkedList源码剖析
- Linux使用wget下载整站
- vs2015,vc6.0连接mysql数据库,出现运行中断异常问题
- 9-17NOIP模拟赛总结
- android studio常用快捷键
- RMQ(Range Minimum Query)
- Android布局加载优化之懒汉模式-ViewStub
- bzoj1026: [SCOI2009]windy数(Dp)
- SpringBoot入门案例