介绍
LinkedList将做为双端链表来实现,它本身实现了List接口和Deque接口,并且实现了所有可选的列表操作,并且允许包含所有元素(包括null)。
下面是LinkedList的类图:
虽然LinkedList有get(int index)方法,但是不推荐通过for循环调用get方法来遍历它,因为get内部实际上是从头部或尾部进行遍历,get一次的时间复杂度是O(n),而ArrayList的get的时间复杂度是O(1)的。如果你要遍历LinkedList,调用iterator函数获取迭代器进行遍历是一个最佳选择。
内部结构
先看看成员变量有哪些
transient int size = 0;/*** Pointer to first node.* Invariant: (first == null && last == null) ||* (first.prev == null && first.item != null)*/transient Node<E> first;/*** Pointer to last node.* Invariant: (first == null && last == null) ||* (last.next == null && last.item != null)*/transient Node<E> last;
size是链表的大小,first指向链表的头结点,last指向链表的尾节点,节点用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;}}
item表示当前节点下存储的数据,next指向下一个节点,prev指向上一个节点,这样通过prev和next指针连接起来就构成了链表。
构造方法
public LinkedList() {}
默认构造和ArrayList有所不同,构造方法是一个空的函数,因为ArrayList底层通过数组保存数组,可以事先指定数组的大小,但是LinkedList并不需要,LinkedList通过prev和next指针来连接每一个节点,它的大小是动态变化的,也不需要扩容。
除此之外,还有第二个构造方法,传入一个Collection集合来初始化链表。
public LinkedList(Collection<? extends E> c) {this();addAll(c);}public boolean addAll(Collection<? extends E> c) {// 这里初始化size是0,从0下标位置进行添加return addAll(size, c);}// 在链表的指定位置前插入一个集合作为链表public boolean addAll(int index, Collection<? extends E> c) {// 检查index的范围checkPositionIndex(index);// 集合转为数组Object[] a = c.toArray();// 获取数组的长度int numNew = a.length;// 长度为0,直接返回,不进行添加if (numNew == 0)return false;// 定义指定插入index位置的节点succ 和succ的上一个节点predNode<E> pred, succ;// 如果添加的位置是在末尾if (index == size) {// 在末尾进行添加,pred赋值为last节点,指定位置node为nullsucc = null;pred = last;} else {// 不是在末尾进行添加,先根据index查到对应的节点再赋值succ = node(index);pred = succ.prev;}// 遍历待添加的元素数组,把元素添加到链表for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;// 构造新节点,值为e,上一个节点是predNode<E> newNode = new Node<>(pred, e, null);// 上一个节点pred等于null,说明前面没有元素,当前节点是头节点if (pred == null)// 把newNode节点赋值给头节点firstfirst = newNode;else// 连接pred和newNode,通过next指针连接起来pred.next = newNode;// 当前节点赋值给上一个节点pred,用于下次遍历pred = newNode;}// 在末尾进行添加的,给尾节点last进行赋值即可if (succ == null) {last = pred;} else {// 不是在末尾进行的添加,还需要连接前后pred.next = succ;succ.prev = pred;}// size大小增加size += numNew;modCount++;return true;}// 根据index索引到对应的node节点Node<E> node(int index) {// assert isElementIndex(index);// 判断下标是在前半部分还是后半部分if (index < (size >> 1)) {// 从first节点顺序遍历查找Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {// 从last节点倒序遍历查找Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}
添加节点
// 在头部插入节点public void addFirst(E e) {linkFirst(e);}// 在尾部插入节点public void addLast(E e) {linkLast(e);}// 在指定位置插入节点public void add(int index, E element) {// check index范围checkPositionIndex(index);// 在尾部插入,直接调用linkLastif (index == size)linkLast(element);else// 在一个节点之前插入,先通过node(index)找到指定位置的节点linkBefore(element, node(index));}// 在指定节点succ前插入节点,值为evoid linkBefore(E e, Node<E> succ) {// assert succ != null;// 获取succ的上一个节点predfinal Node<E> pred = succ.prev;// 构造新的节点final Node<E> newNode = new Node<>(pred, e, succ);// succ的上一个节点指针prev指向新节点newNodesucc.prev = newNode;// 如果pred为null,说明succ之前本来没有节点,那么新的节点插入进来就是头节点了if (pred == null)// 头节点赋值first = newNode;else// 把pred的next指针指向新的节点,插入完毕pred.next = newNode;// 大小+1size++;modCount++;}// 在头部插入节点private void linkFirst(E e) {// 获取当前头节点的值final Node<E> f = first;// 构造新插入的节点,他的下一个节点是原来的头节点ffinal Node<E> newNode = new Node<>(null, e, f);// 把新节点作为头节点first = newNode;// 原来没有头节点,说明之前链表是空的if (f == null)// 新节点是第一个节点,所以尾节点也是它last = newNode;else// 原来的头节点不是空,把之前头节点的prev指针指向新的节点f.prev = newNode;// 大小+1size++;modCount++;}// 在尾部插入节点void linkLast(E e) {// 获取尾节点final Node<E> l = last;// 构造新插入的节点,上一个节点是原来的尾节点lfinal Node<E> newNode = new Node<>(l, e, null);// 把新节点作为尾节点last = newNode;// 原来没有尾节点,说明之前链表为空if (l == null)// 新节点是第一个节点,头节点也是它first = newNode;else// 原来的尾节点不是空,把之前尾节点的next指针指向新的节点l.next = newNode;// 大小+1size++;modCount++;}
移除节点
// 移除并返回第一个元素public E removeFirst() {final Node<E> f = first;if (f == null)throw new NoSuchElementException();return unlinkFirst(f);}// 移除并返回最后一个元素public E removeLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return unlinkLast(l);}// 移除指定位置元素并返回public E remove(int index) {checkElementIndex(index);return unlink(node(index));}// 移除头节点fprivate E unlinkFirst(Node<E> f) {// assert f == first && f != null;// 先获取节点的值,用于返回final E element = f.item;// 获取f节点的下一个节点nextfinal Node<E> next = f.next;// 清空节点的值f.item = null;// 清除next指针f.next = null; // help GC// 头节点变为next节点first = next;// 如果next节点为null,说明移除后链表没有节点了if (next == null)// last节点也是nulllast = null;else// next节点是头节点,所以next的prev指针指向nullnext.prev = null;// 大小-1size--;modCount++;return element;}// 移除尾节点private E unlinkLast(Node<E> l) {// assert l == last && l != null;// 先获取节点的值,用于返回final E element = l.item;// 获取l节点的上一个节点prevfinal Node<E> prev = l.prev;// 清空节点的值l.item = null;// 清空prev指针l.prev = null; // help GC// 尾节点变为尾节点的上一个节点prevlast = prev;// 上一个节点为null,说明移除之后链表就没有节点了if (prev == null)// 头节点也要赋值为nullfirst = null;else// prev作为新的尾节点,它的next需要指向nullprev.next = null;// 大小-1size--;modCount++;return element;}// 移除指定元素xE unlink(Node<E> x) {// assert x != null;// 获取指定元素x的值final E element = x.item;// 获取x的下一个节点nextfinal Node<E> next = x.next;// 获取x的上一个节点prevfinal Node<E> prev = x.prev;// 上一个节点为空,说明要移除的节点x是头节点if (prev == null) {// 移除后头节点应该是x的下一个节点nextfirst = next;} else {// 更改上一个节点的next指针,指向x的nextprev.next = next;// 断开x与prev节点之间的连接x.prev = null;}// 下一个节点为空,说明要移除的节点x是尾节点if (next == null) {// 移除后尾节点应该是x的上一个节点prevlast = prev;} else {// 更改下一个节点的prev指针,指向x的prevnext.prev = prev;// 断开x与next节点之间的连接x.next = null;}// x前后节点的指针都已经断开,接着把值item赋值为null,可以进行GCx.item = null;// 大小-1size--;modCount++;return element;}
迭代器
LinkedList的迭代器有两种,一种是ListIterator顺序迭代,一种是DescendingIterator降序迭代。
private class ListItr implements ListIterator<E> {private Node<E> lastReturned;private Node<E> next;private int nextIndex;private int expectedModCount = modCount;ListItr(int index) {// assert isPositionIndex(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;elsenextIndex--;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);elselinkBefore(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();}}private class DescendingIterator implements Iterator<E> {private final ListItr itr = new ListItr(size());public boolean hasNext() {return itr.hasPrevious();}public E next() {return itr.previous();}public void remove() {itr.remove();}}
