LinkedList 特性 1 2 public class LinkedList <E> extends AbstractSequentialList <E> implements List <E>, Deque<E>, Cloneable, java.io.Serializable
1、继承于 AbstractSequentialList ,本质上面与继承 AbstractList 没有什么区别,AbstractSequentialList 完善了 AbstractList 中没有实现的方法。
2、Serializable:成员变量 Node 使用 transient 修饰,通过重写read/writeObject 方法实现序列化。
3、Cloneable:重写clone()方法,通过创建新的LinkedList 对象,遍历拷贝数据进行对象拷贝。
4、Deque:实现了Collection 大家庭中的队列接口,说明他拥有作为双端队列的功能。
LinkedList与ArrayList最大的区别就是LinkedList中实现了Collection中的 Queue(Deque)接口 拥有作为双端队列的功能
基本属性 链表没有长度限制,他的内存地址不需要分配固定长度进行存储,只需要记录下一个节点的存储地址即可完成整个链表的连续。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 transient int size = 0 ;transient Node<E> first;transient Node<E> last;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 在1.6 版本以及之前,只通过一个 header 头指针保存队列头和尾。这种操作可以说很有深度,但是从代码阅读性来说,却加深了阅读代码的难度。因此在后续的JDK 更新中,将头节点和尾节点 区分开了。节点类也更名为 Node。
为什么Node这个类是静态的?答案是:这跟内存泄露有关,Node类是在LinkedList类中的,也就是一个内部类,若不使用static修饰,那么Node就是一个普通的内部类,在java中,一个普通内部类在实例化之后,默认会持有外部类的引用,这就有可能造成内存泄露(内部类与外部类生命周期不一致时)。但使用static修饰过的内部类(称为静态内部类),就不会有这种问题
非静态内部类会自动生成一个构造器依赖于外部类:也是内部类可以访问外部类的实例变量的原因
静态内部类不会生成,访问不了外部类的实例变量,只能访问类变量
构造器 1 2 3 4 5 6 public LinkedList () {} public LinkedList (Collection<? extends E> c) { this (); addAll(c); }
添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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; if (l == null ) first = newNode; else l.next = newNode; size++; modCount++; } public void add (int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } private void checkPositionIndex (int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } private boolean isPositionIndex (int index) { return index >= 0 && index <= size; } 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; } } 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 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; 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; } if (succ == null ) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true ; }
final修饰,不希望在运行时对变量做重新赋值
LinkedList 在插入数据优于ArrayList ,主要是因为他只需要修改指针的指向即可,而不需要将整个数组的数据进行转移。而LinkedList 由于没有实现 RandomAccess,或者说不支持索引搜索的原因,他在查找元素这一操作,需要消耗比较多的时间进行操作(n/2)。
删除元素 1、AbstractSequentialList的remove 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } public boolean remove (Object o) { if (o == null ) { for (Node<E> x = first; x != null ; x = x.next) { if (x.item == null ) { unlink(x); return true ; } } } else { for (Node<E> x = first; x != null ; x = x.next) { if (o.equals(x.item)) { unlink(x); return true ; } } } return false ; } E unlink (Node<E> x) { final E element = x.item; final Node<E> next = x.next; final Node<E> prev = 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--; modCount++; return element; }
2、Deque 中的Remove 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private E unlinkFirst (Node<E> f) { final E element = f.item; final Node<E> next = f.next; f.item = null ; f.next = null ; first = next; if (next == null ) last = null ; else next.prev = null ; size--; modCount++; return element; }
ListIterator 迭代器:按次序遍历
普通迭代器:next
ListIterator扩展了普通的迭代器
游标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 public interface ListIterator <E> extends Iterator <E> { ... } private class ListItr implements ListIterator <E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; 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; } public boolean hasPrevious () { return nextIndex > 0 ; } public E previous () { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException (); lastReturned = next = (next == null ) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex () { return nextIndex; } public int previousIndex () { return nextIndex - 1 ; } public void remove () { checkForComodification(); if (lastReturned == null ) throw new IllegalStateException (); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null ; expectedModCount++; } public void set (E e) { if (lastReturned == null ) throw new IllegalStateException (); checkForComodification(); lastReturned.item = e; } public void add (E e) { checkForComodification(); lastReturned = null ; if (next == null ) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } public void forEachRemaining (Consumer<? super E> action) { Objects.requireNonNull(action); while (modCount == expectedModCount && nextIndex < size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); } final void checkForComodification () { if (modCount != expectedModCount) throw new ConcurrentModificationException (); } }
双端链表(队列Queue) java中队列的实现就是LinkedList: 我们之所以说LinkedList 为双端链表,是因为他实现了Deque 接口;我们知道,队列是先进先出的,添加元素只能从队尾添加,删除元素只能从队头删除,Queue中的方法就体现了这种特性。 支持队列的一些操作,我们来看一下有哪些方法实现:
pop()是栈结构的实现类的方法,返回的是栈顶元素,并且将栈顶元素删除
poll()是队列的数据结构,获取队头元素并且删除队头元素
push()是栈结构的实现类的方法,把元素压入到栈中
peek()获取队头元素 ,但是不删除队列的头元素
offer()添加队尾元素
可以看到Deque 中提供的方法主要有上述的几个方法,接下来我们来看看在LinkedList 中是如何实现这些方法的。
1.1、队列的增 offer()添加队尾元素
1 2 3 public boolean offer (E e) { return add(e); }
具体的实现就是在尾部添加一个元素
1.2、队列的删 poll()是队列的数据结构,获取对头元素并且删除队头元素
1 2 3 4 public E poll () { final Node<E> f = first; return (f == null ) ? null : unlinkFirst(f); }
具体的实现前面已经讲过,删除的是队列头部的元素
1.3、队列的查 peek()获取队头元素 ,但是不删除队列的头元素
1 2 3 4 public E peek () { final Node<E> f = first; return (f == null ) ? null : f.item; }
1.4、栈的增 push()是栈结构的实现类的方法,把元素压入到栈中
push() 方法的底层实现,其实就是调用了 addFirst(Object o)
1 2 3 public void push (E e) { addFirst(e); }
1.5、栈的删 pop()是栈结构的实现类的方法,返回的是栈顶元素,并且将栈顶元素删除
1 2 3 4 5 6 7 8 9 public E pop () { return removeFirst(); } public E removeFirst () { final Node f = first; if (f == null ) throw new NoSuchElementException (); return unlinkFirst(f); }