JDK容器与并发—List—LinkedList

来源:互联网 发布:程序员都用什么浏览器 编辑:程序博客网 时间:2024/05/22 06:31

概述

      基于双向链表的List实现,也实现了Deque接口,无界List。

1)非线程安全;

2)fail-fast:同ArrayList。

数据结构

      基于非环形双链表,维护first、last引用

/** * 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;// 双链表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;}}

若first=last=null,则链表为空,首尾节点的item不能为null。

构造器

// 无参构造public LinkedList() {}// 指定Collection参数构造,将其中的元素添加进来public LinkedList(Collection<? extends E> c) {this();addAll(c);}

增删改查

基础方法

      基础方法中涉及到链表首、尾、中间的插入与删除,都是关于Node的prev、next引用的操作,所以动态插入、删除都是很方便的。由于LinkedList维护了first、last引用,所以在链表首尾进行插入、删除是高效的,但是在链表中间插入、删除都需要先根据索引遍历获取到索引位置节点,所以有一个遍历成本。

// 如果链表只有一个节点,则first=last=node,其中node为节点// 在链表头添加节点private void linkFirst(E e) {final Node<E> f = first;final Node<E> newNode = new Node<>(null, e, f);first = newNode; // 维护first,指向新增节点if (f == null)last = newNode; // 链表为空,维护last,指向新增节点elsef.prev = newNode; // 将旧的链表头的prev指向新增节点size++;modCount++; // 结构性修改次数加1}// 在链表尾添加节点void linkLast(E e) {final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode; // 维护last,指向新增节点if (l == null)first = newNode; // 链表为空,维护first,指向新增节点elsel.next = newNode; // 将旧的链表尾的next指向新增节点size++;modCount++;}// 在链表中间插入节点void linkBefore(E e, Node<E> succ) {// assert succ != null;final Node<E> pred = succ.prev;final Node<E> newNode = new Node<>(pred, e, succ);succ.prev = newNode; // 将succ的prev指向新增节点if (pred == null)first = newNode; // succ为旧的链表首节点,维护first,指向新增节点elsepred.next = newNode; // 将新增节点插入进来size++;modCount++;}// 在链表首删除节点private E unlinkFirst(Node<E> f) {// assert f == first && f != null;final E element = f.item;final Node<E> next = f.next;f.item = null;f.next = null; // help GCfirst = next;if (next == null)last = null; // 链表为空,维护first、lastelsenext.prev = null; // 更新链表首节点size--;modCount++;return element;}// 在链表尾删除节点private E unlinkLast(Node<E> l) {// assert l == last && l != null;final E element = l.item;final Node<E> prev = l.prev;l.item = null;l.prev = null; // help GClast = prev;if (prev == null)first = null; // 链表为空,维护first、lastelseprev.next = null; // 更新链表尾节点size--;modCount++;return element;}// 在链表中间删除节点E unlink(Node<E> x) {// assert x != null;final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;if (prev == null) {first = next; // 删除的节点为旧的链表首节点,维护first指向新的节点} else {prev.next = next; // 更新prev的nextx.prev = null; // help GC}if (next == null) {last = prev; // 删除的节点为旧的链表尾节点,维护last指向新的节点 } else {next.prev = prev; // 更新next的prevx.next = null; // help GC}x.item = null; // 清空itemsize--;modCount++;return element;}

      涉及到的方法有:

// List methodspublic boolean add(E e)public void add(int index, E element)public boolean addAll(Collection<? extends E> c)public boolean addAll(int index, Collection<? extends E> c)// Deque methodspublic void addFirst(E e)public void addLast(E e)public boolean offerFirst(E e)public boolean offerLast(E e)// Queue methodspublic boolean offer(E e)// Stack methodspublic void push(E e)

由于LinkedList是无界的,关于添加元素的方法大同小异,都用到了基础方法来做。重点看下随机add、addAll:

public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));}// 将index与一半容量比较,从first或last最快遍历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;}}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;}// 将数组a转化为一个双链表for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;Node<E> newNode = new Node<>(pred, e, null);if (pred == null)first = newNode;elsepred.next = newNode;pred = newNode;}if (succ == null) {last = pred;} else {pred.next = succ;succ.prev = pred;}size += numNew;modCount++;return true;}

随机插入都需要根据索引遍历获取索引位置的节点,然后在其前面插入,维护好prev、next引用。

涉及到的方法有:

// List methodspublic E remove(int index)public boolean remove(Object o)// Deque methodspublic E removeFirst()public E removeLast()public E pollFirst()public E pollLast()// Queue methodspublic E remove()public E poll()// Stack methodspublic E pop()

删除节点就是用到unlink。

      来自List接口,这里是随机set:

public E set(int index, E element) {        checkElementIndex(index);        Node<E> x = node(index);        E oldVal = x.item;        x.item = element;        return oldVal;    }

涉及到的方法有:

// List methodspublic E get(int index)// Deque methodspublic E getFirst()public E getLast()public E peekFirst()public E peekLast()// Queue methodspublic E element()public E peek()

迭代器

public ListIterator<E> listIterator(int index) {checkPositionIndex(index);return new ListItr(index);}private class ListItr implements ListIterator<E>

在双链表的基础上实现了ListIterator,支持从索引位置遍历,另外还提供了与ListIterator遍历顺序相反的迭代器,利用了代理设计模式:

public Iterator<E> descendingIterator() {return new DescendingIterator();}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();}}

特性

      List中的随机插入、删除、修改、访问都需要先根据索引遍历获取到索引位置的节点,再进行操作,有遍历成本,由于维护了first、last引用,所以在链表的首尾操作是高效的。

0 0