JDK源码LinkedList
Posted sqy123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码LinkedList相关的知识,希望对你有一定的参考价值。
引用博文链接 : https://www.cnblogs.com/leskang/p/6029780.html
LinkedList继承了 AbstractSequentialList抽象类,而不是像 ArrayList和 Vector那样实现 AbstractList,正如其名,它提供了对序列的连续访问的抽象:
LinkedList的底层是 Deque双向链表,实现了 Deque接口,而 Deque接口继承于 Queue接口,因此,在java中,如果要实现队列,一般都使用 LinkedList来实现。
LinkedList是非线程安全的,
1 public class LinkedList<E> 2 extends AbstractSequentialList<E> 3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable 4 { 5 6 //LinkedList中元素个数 7 transient int size = 0; 8 9 10 11 //默认构造函数:创建一个空的链表 12 public LinkedList() { 13 } 14 15 ..... 16 }
Node节点:
Node节点 一共有三个属性:item代表节点值,prev代表节点的前一个节点,next代表节点的后一个节点。
每个结点都有一个前驱和后继节点。
1 private static class Node<E> { 2 E item; 3 Node<E> next; 4 Node<E> prev; 5 6 Node(Node<E> prev, E element, Node<E> next) { 7 this.item = element; 8 this.next = next; 9 this.prev = prev; 10 } 11 } 12 13 14 //指向链表的第一个节点 15 transient Node<E> first; 16 17 //指向链表的最后一个节点 18 transient Node<E> last;
add(E e):
1 public boolean add(E e) { 2 linkLast(e); 3 return true; 4 } 5 6 void linkLast(E e) { 7 final Node<E> l = last;//链表最后一个节点 8 final Node<E> newNode = new Node<>(l, e, null);//建立节点对象,前一个(perv属性)是last,后一个(next属性)是null,中间item数据是输入的参数e 9 last = newNode; 10 if (l == null) 11 first = newNode; //如果最后一个节点 为null表示链表为空,则添加数据时将新加的数据作为第一个节点 12 else 13 l.next = newNode; //如果链表不为空则将最后一个链表的next属性指向新添加的节点 14 size++; //链表长度自增1 15 modCount++; 16 }
LInkedList添加操作时每个新添加的对象都会被放到新建的Node对象中,Node对象中包含加入的对象和前后指针属性(pred和next,pred和next其实都是Node对象,直接存放的就是当前对象的前一个节点对象和后一个节点对象,最后一个及节点的next值为null),然后将原来最后一个last节点的next指针指向新加入的对象,即可!
void addFirst(E e)方法,在链表的头添加一个新节点对象;void add(int index, E element) ,在链表的index位置添加一个新节点对象;void addLast(E e),在链表的尾部添加一个新节点对象。
addAll:
1 public boolean addAll(Collection<? extends E> c) { 2 return addAll(size, c); 3 } 4 5 6 public boolean addAll(int index, Collection<? extends E> c) { 7 checkPositionIndex(index);//判断index是否越界,越界则抛出异常 8 Object[] a = c.toArray(); 9 int numNew = a.length;//要插入的集合的长度 10 if (numNew == 0) 11 return false; 12 Node<E> pred, succ;//声明pred和succ两个Node对象,用于标识要插入元素的前一个节点和后一个节点 13 if (index == size) { //如果size等于原数组长度则表示在结尾添加 14 succ = null; 15 pred = last; 16 } else { 17 succ = node(index);//index位置上的Node对象 18 pred = succ.prev; 19 } 20 for (Object o : a) { //遍历要插入的集合 21 @SuppressWarnings("unchecked") E e = (E) o; 22 Node<E> newNode = new Node<>(pred, e, null); 23 if (pred == null) 24 first = newNode; //如果要插入的位置的前一个节点为null表示是第一个节点,则直接将newNode赋给第一个节点 25 else 26 pred.next = newNode; //将要插入的集合元素节点对象赋给此位置原节点对象的前一个对象的后一个,即更改前一个节点对象的next指针指到新插入的节点上 27 pred = newNode;//更改指向后将新节点对象赋给pred作为下次循环中新插入节点的前一个对象节点,依次循环 28 } 29 //此时pred代表集合元素的插入完后的最后一个节点对象 30 if (succ == null) { //结尾添加的话在添加完集合元素后将最后一个集合的节点对象pred作为last 31 last = pred; 32 } else { 33 pred.next = succ;//将集合元素的最后一个节点对象的next指针指向原index位置上的Node对象 34 succ.prev = pred;//将原index位置上的pred指针对象指向集合的最后一个对象 35 } 36 size += numNew; 37 modCount++; 38 return true; 39 }
LinkedList在某个位置插入元素是通过将原位置节点的前一个节点的后一个指针指向新插入的元素,然后将原位置的节点的前一个指针指向新元素来实现的,相当于插入元素后后面的元素后移了,但是不是像ArrayList那样将所有插入位置后面的元素都后移,此处只是改变其前后节点的指向。所以LinkedList修改的速度比ArrayList快!
删除:
1 public E remove(int index) { 2 checkElementIndex(index); 3 return unlink(node(index)); 4 } 5 6 E unlink(Node<E> x) { 7 final E element = x.item; 8 final Node<E> next = x.next;//传入节点的下一个节点 9 final Node<E> prev = x.prev;//传入节点的上一个节点 10 if (prev == null) { 11 first = next; 12 } else { 13 prev.next = next;//将传入的节点的下一个节点对象赋给其之前前一个节点的下一个节点对象,即将传入的节点对象跳过 14 x.prev = null;//将传入对象的前一个节点对象置空使其前一个指针不指向任何元素 15 } 16 if (next == null) { 17 last = prev;//如果next为null表示是最后一个元素,直接将pred节点赋给last 18 } else { 19 next.prev = prev; 20 x.next = null;//将传入节点的后一个节点置空,使其后一个节点指针不指向任何元素 21 } 22 x.item = null;//将传入的节点对象上的对象置空,也就是整个Node对象中的属性都为空了,后面会被GC回收 23 size--; 24 modCount++; 25 return element; 26 } 27 28 //查询指定节点 29 Node<E> node(int index) { 30 if (index < (size >> 1)) { //判断index是否小于size的一半,如果小于则从头遍历节点,否则从结尾遍历节点 31 Node<E> x = first; 32 for (int i = 0; i < index; i++) 33 x = x.next; //从first第一个节点开始,依次将后一个节点赋给x 34 return x; //返回index位置上的Node对象,下同理 35 } else { 36 Node<E> x = last; 37 for (int i = size - 1; i > index; i--) 38 x = x.prev; 39 return x; 40 } 41 }
LinkedList删除操作是通过将index位置上的前一个节点的next执行index位置的后一个节点,同时将后一个节点的pred指向前一个节点,然后将index位置上的Node节点对象属性都置空来实现的,置空后的Node对象会被GC垃圾回收期回收掉。
E removeFirst() ,删除链表第一个节点对象, E removeLast() ,删除链表最后一个节点对象。
1 public boolean remove(Object o) { //删除具体节点对象item 2 if (o == null) { 3 for (Node<E> x = first; x != null; x = x.next) { 4 if (x.item == null) { 5 unlink(x); 6 return true; 7 } 8 } 9 } else { 10 for (Node<E> x = first; x != null; x = x.next) { 11 if (o.equals(x.item)) { 12 unlink(x); 13 return true; 14 } 15 } 16 } 17 return false; 18 }
替换:
1 public E set(int index, E element) { 2 checkElementIndex(index); 3 Node<E> x = node(index);//获取index位置上的节点对象 4 E oldVal = x.item; 5 x.item = element;//将新插入的元素直接赋给此位置上节点对象的item属性即可 6 return oldVal; 7 }
LinkedList中的实际数据保存在节点对象的item属性中,节点的另外两个属性不变,这再次说明LinkedList修改的速度比ArrayList修改速度快!
查询:
1 public E get(int index) { 2 checkElementIndex(index); 3 return node(index).item; //获取节点对象的item属性 4 }
查询操作是通过Node<E> node(int index)方法实现的,该方法是通过先判断index位置是在整个链表的前一半还是后一半来遍历前一半或者后一半元素来获取index位置上的元素,
说明LinkedList查询速度比ArrayList慢!
E getFirst() ,查询链表第一个节点对象的item, E getLast() ,查询链表最后一个节点对象的item,
包含:
1 public boolean contains(Object o) { 2 return indexOf(o) != -1; 3 } 4 5 public int indexOf(Object o) { 6 int index = 0; 7 if (o == null) { //如果对象是null 8 for (Node<E> x = first; x != null; x = x.next) { 9 if (x.item == null) 10 return index; 11 index++; 12 } 13 } else { 14 for (Node<E> x = first; x != null; x = x.next) { 15 if (o.equals(x.item)) 16 return index; 17 index++; 18 } 19 } 20 return -1; 21 }
clear:
1 public void clear() { 2 // Clearing all of the links between nodes is "unnecessary", but: 3 // - helps a generational GC if the discarded nodes inhabit 4 // more than one generation 5 // - is sure to free memory even if there is a reachable Iterator 6 for (Node<E> x = first; x != null; ) { 7 Node<E> next = x.next; 8 x.item = null; 9 x.next = null; 10 x.prev = null; 11 x = next; 12 } 13 first = last = null; 14 size = 0; 15 modCount++; 16 }
clear()会把节点所有属性都置为null,确保资源得到释放,只是清空了LinkedList对于里面所含有对象指针的释放。
LinkedList只是把自己中每个对象的引用设为null没错,那些不再被引用的对象是会被回收了,但是如果有一些对象外部还在继续用的话,也是回收不了的。例如:
1 public static void main(String[] args) { 2 LinkedList<String> linklist = new LinkedList<>(); 3 String se ="gg"; 4 linklist.add("a"); 5 linklist.add("b"); 6 linklist.add(se); 7 linklist.clear(); 8 System.out.println("长度:"+linklist.size()); 9 System.out.println("内容:"+linklist.toString()); 10 System.out.println("se:"+se); 11 }
结果:
1 长度:0 2 内容:[] 3 se:gg
Queue operations
1 //获取但不移除链表的头部第一个节点对象的item 2 public E peek() { 3 final Node<E> f = first; 4 return (f == null) ? null : f.item;//如果第一个节点对象为null,返回null 5 } 6 7 //获取但不移除链表的头部第一个节点对象的item 8 public E element() { 9 return getFirst(); 10 } 11 12 public E getFirst() { 13 final Node<E> f = first; 14 if (f == null) 15 throw new NoSuchElementException();//如果第一个节点对象为null,返回异常信息 16 return f.item; 17 } 18 19 //获取并且移除链表的头部第一个节点对象的item 20 public E poll() { 21 final Node<E> f = first; 22 return (f == null) ? null : unlinkFirst(f); 23 } 24 25 private E unlinkFirst(Node<E> f) { 26 // assert f == first && f != null; 27 final E element = f.item; 28 final Node<E> next = f.next; 29 f.item = null; //第一个节点对象item,next置为null 30 f.next = null; // help GC 31 first = next; 32 if (next == null) 33 last = null; 34 else 35 next.prev = null; //下一个节点对象的prev置为null 36 size--; 37 modCount++; 38 return element; 39 } 40 41 //获取并且移除链表的头部第一个节点对象的item 42 public E remove() { 43 return removeFirst(); 44 } 45 46 public E removeFirst() { 47 final Node<E> f = first; 48 if (f == null) 49 throw new NoSuchElementException(); 50 return unlinkFirst(f); 51 } 52 53 //在链表的尾部添加一个新节点对象 54 public boolean offer(E e) { 55 return add(e); 56 }
Deque operations 双向链表操作
LinkedList implements Deque<E>,interface Deque<E> extends Queue<E>,所以Deque operation的很多方法和Queue operation的方法一样,例如E peekFirst() 与E peek() 功能一样。
1 //从链表头部遍历,移除第一出现节点对象的item==o的节点 2 public boolean removeFirstOccurrence(Object o) { 3 return remove(o); 4 } 5 6 //从链表尾部遍历,移除第一出现节点对象的item==o的节点 7 public boolean removeLastOccurrence(Object o) { 8 if (o == null) { 9 for (Node<E> x = last; x != null; x = x.prev) { 10 if (x.item == null) { 11 unlink(x); 12 return true; 13 } 14 } 15 } else { 16 for (Node<E> x = last; x != null; x = x.prev) { 17 if (o.equals(x.item)) { 18 unlink(x); 19 return true; 20 } 21 } 22 } 23 return false; 24 }
ListIterator<E> listIterator(int index):
1 //返回此列表中的元素的列表迭代器(按适当顺序:从列表中指定位置开始) 2 public ListIterator<E> listIterator(int index) { 3 checkPositionIndex(index); 4 return new ListItr(index); //调用内部类ListItr的匿名对象 5 } 6 7 //把ListIterator接口送给内部类实现是为了与Iterator接口兼容,因为ListIterator接口继承自Iterator接口 8 private class ListItr implements ListIterator<E> { //实现了ListIterator接口 9 private Node<E> lastReturned; //指向上一个返回得到的元素 10 private Node<E> next; //指向下一个未涉足的元素 11 private int nextIndex; 12 // modCount :已从结构上修改 此列表的次数。从结构上修改是指更改列表的大小, 13 //或者打乱列表,从而使正在进行的迭代产生错误的结果。 14 //在迭代期间面临并发修改时,它提供了快速失败 行为,而不是非确定性行为。 15 private int expectedModCount = modCount; 16 17 ListItr(int index) { 18 // assert isPositionIndex(index); 19 next = (index == size) ? null : node(index); 20 nextIndex = index; 21 } 22 23 public boolean hasNext() { 24 return nextIndex < size; 25 } 26 27 public E next() { 28 checkForComodification(); 29 if (!hasNext()) 30 throw new NoSuchElementException(); 31 32 lastReturned = next; 33 next = next.next; 34 nextIndex++; 35 return lastReturned.item; 36 } 37 38 public boolean hasPrevious() { 39 return nextIndex > 0; 40 } 41 42 public E previous() { 43 checkForComodification(); 44 if (!hasPrevious()) 45 throw new NoSuchElementException(); 46 47 lastReturned = next = (next == null) ? last : next.prev; 48 nextIndex--; 49 return lastReturned.item; 50 } 51 52 public int nextIndex() { 53 return nextIndex; 54 } 55 56 public int previousIndex() { 57 return nextIndex - 1; 58 } 59 60 public void remove() { 61 checkForComodification(); 62 if (lastReturned == null) 63 throw new IllegalStateException(); 64 65 Node<E> lastNext = lastReturned.next; 66 unlink(lastReturned); //删除外部元素modCount++ 67 if (next == lastReturned) 68 next = lastNext; 69 else 70 nextIndex--; 71 lastReturned = null; 72 expectedModCount++; //所以内部的expectedModCount也++来同 73 } 74 75 public void set(E e) { 76 if (lastReturned == null) 77 throw new IllegalStateException(); 78 checkForComodification(); 79 lastReturned.item = e; 80 } 81 82 public void add(E e) { 83 checkForComodification(); 84 lastReturned = null; 85 if (next == null) 86 linkLast(e); 87 else 88 linkBefore(e, next); 89 nextIndex++; 90 expectedModCount++; 91 } 92 93 public void forEachRemaining(Consumer<? super E> action) { 94 Objects.requireNonNull(action); 95 while (modCount == expectedModCount && nextIndex < size) { 96 action.accept(next.item); 97 lastReturned = next; 98 next = next.next; 99 nextIndex++; 100 } 101 checkForComodification(); 102 } 103 104 //外部结构修改则迭代快速失败fast-fails 105 final void checkForComodification() { 106 if (modCount != expectedModCount) 107 throw new ConcurrentModificationException(); 108 } 109 }
ListIterator<E> listIterator(int index),得到从index开始的迭代器(可以正向,也可以反向)
正向:
1 public static void main(String[] args) { 2 LinkedList<String> list = new LinkedList<>(); 3 list.add("abc1"); 4 list.add("abc2"); 5 list.add("abc3"); 6 list.add("abc4"); 7 ListIterator<String> listit = list.listIterator(2); 8 while(listit.hasNext()) { 9 System.out.println("第一次" + listit.next()); 10 } 11 }
结果:
1 第一次abc3 2 第一次abc4
反向:
1 public static void main(String[] args) { 2 LinkedList<String> list = new LinkedList<>(); 3 list.add("abc1"); 4 list.add("abc2"); 5 list.add("abc3"); 6 list.add("abc4"); 7 list.add("abc5"); 8 ListIterator<String> listit = list.listIterator(2); 9 while(listit.hasPrevious()){ 10 System.out.println(listit.previous()); 11 } 12 }
结果:
1 abc2 2 abc1
listIterator可以实现对象的删除,添加,但是Iterator可以遍历,删除
1 public static void main(String[] args) { 2 LinkedList<String> list = new LinkedList<>(); 3 list.add("abc1"); 4 list.add("abc2"); 5 list.add("abc3"); 6 list.add("abc4"); 7 ListIterator<String> listit = list.listIterator(); 8 while(listit.hasNext()){ 9 if(listit.next().equals("abc3")){ 10 listit.remove(); 11 listit.add("itcast"); 12 } 13 } 14 System.out.println(list); 15 }
结果:
1 [abc1, abc2, itcast, abc4]
Iterator:
1 public static void main(String[] args) { 2 LinkedList<String> list = new LinkedList<>(); 3 list.add("abc1"); 4 list.add("abc2"); 5 list.add("abc3"); 6 list.add("abc4"); 7 Iterator<String> listit = list.iterator(); 8 while(listit.hasNext()){ 9 if(listit.next().equals("abc3")){ 10 listit.remove(); 11 list.add("new"); 12 break; //添加成功以后直接break;不继续判断就可以不出现异常 13 } 14 } 15 System.out.println(list); 16 }
部分功能:
1 //反向迭代器 2 public Iterator<E> descendingIterator() { 3 return new DescendingIterator(); 4 } 5 6 private class DescendingIterator implements Iterator<E> { 7 private final ListItr itr = new ListItr(size()); 8 public boolean hasNext() { 9 return itr.hasPrevious(); 10 } 11 public E next() { 12 return itr.previous(); 13 } 14 public void remove() { 15 itr.remove(); 16 } 17 } 18 19 @SuppressWarnings("unchecked") 20 private LinkedList<E> superClone() { 21 try { 22 return (LinkedList<E>) super.clone(); 23 } catch (CloneNotSupportedException e) { 24 throw new InternalError(e); 25 } 26 } 27 28 //clone链表但是节点对象是添加,而不是clone 29 public Object clone() { 30 LinkedList<E> clone = superClone(); 31 32 // Put clone into "virgin" state 33 clone.first = clone.last = null; 34 clone.size = 0; 35 clone.modCount = 0; 36 37 // Initialize clone with our elements 38 for (Node<E> x = first; x != null; x = x.next) 39 clone.add(x.item); 40 41 return clone; 42 } 43 44 //包含链表节点对象item的数组 45 public Object[] toArray() { 46 Object[] result = new Object[size]; 47 int i = 0; 48 for (Node<E> x = first; x != null; x = x.next) 49 result[i++] = x.item; 50 return result; 51 }
Spliterator 是 Java 8 引入的新接口,顾名思义,Spliterator 可以理解为 Iterator 的 Split 版本(但用途要丰富很多)。
使用 Iterator 的时候,我们可以顺序地遍历容器中的元素,使用 Spliterator 的时候,我们可以将元素分割成多份,分别交于不于的线程去遍历,以提高效率。
使用 Spliterator 每次可以处理某个元素集合中的一个元素 — 不是从 Spliterator 中获取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法对元素应用操作。
但 Spliterator 还可以用于估计其中保存的元素数量,而且还可以像细胞分裂一样变为一分为二。这些新增加的能力让流并行处理代码可以很方便地将工作分布到多个可用线程上完成。
转自 http://blog.sina.com.cn/s/blog_3fe961ae0102wxdb.html
以上是关于JDK源码LinkedList的主要内容,如果未能解决你的问题,请参考以下文章