LinkedList源码详解(基于jdk1.8.0_231)

1. LinkedList简介

  • LinkedList数据结构为双链表, 继承了AbstractSequentialList 实现了List, Deque, Cloneable, java.io.Serializable接口;
  • 允许所有元素为null;
  • LinkedList多线程环境下,不是线程安全的,需要外部加锁或使用其他线程安全的链表替代或使用Collettions.synchronizedList(new LinkedList(...))包装;
  • 与ArrayList一样,Iterator,listIteratot两种迭代器都有fail-fast机制,提醒业务开发程序员,目前可能是多线程环境,使用LinkedList不安全;
  • 与ArrayList不同,LinkedList由于是双端链表,一般地,LinkedList头尾插入和删除时间复杂为O(1),查找的时间复杂度为O(n),而ArrayList的头插入和删除时间复杂度为O(n),查找时间为O(1),所以在大量删除插入操作中推荐使用LinkedList,大量查找操作中推荐使用ArrayList.
  • 相比于ArrayList,LinkedList在并行操作时,会有一些操作要求,ArrayList很容易并行.
  • LinkedList没有所谓的扩容机制,不考虑虚拟内存情况下,内存余量多大,他可扩多大;
  • 根据“实现了啥接口,就必须提供啥服务”原则,LinkedList实现Deque接口,则LinkedList一定可以做双端队列,当然也可以当普通队列啦。
  • LinkedList实现了大量相似的方法,如增加节点 add addLast offer offerLast 这些方法都是将节点插入LinkedList尾部,我习惯于根据LinkedList实际场合的扮演的数据结构,使用对应的方法,或根据具体情况需要啥返回值采用对应的API。具体来讲,当LinkedList当单向链表,我习惯使用add添加元素,当linkedList当双向链表,我习惯使用addLast addFist添加元素,当LinkedList当单向队列,我习惯使用offer方法,当LinkedList当双向队列,我习惯使用offerLast offerFirst方法。在阅读完源码后,会给出相关的总结。“是啥数据结构,提供啥语义接口”这样我们的代码可读性才比较好。

2. LinkedList逻辑内存模型


根据LinkedList源码,我们称包含pre item next三个域的结构为一个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;
        }
    }

3. LinkedList UML简图

4. LinkedList API简介

自定义的字段

transient int size = 0;
transient Node<E> first; //头指针(对头结点的引用)
transient Node<E> last;  //尾指针(对尾结点的引用)
private static final long serialVersionUID = 876323262645176354L;

直接继承的字段

protected transient int modCount = 0; //继承自AbstractList

构造函数

public LinkedList()
public LinkedList(Collection<? extends E> c)

override或新增的API(public 方法)

-----------------增加节点的方法---------------------
void                addFirst(E e)  //头插法,插入的新元素为头结点
void                addLast(E e)   //尾插法,插入的新元素为尾节点
boolean             add(E e)   //尾插法,插入的新元素为尾节点
void                add(int index, E element)
boolean             addAll(Collection<? extends E> c)
boolean             addAll(int index,Collection<? extends E> c)
-----------------删除节点的方法---------------------
E                   remove(int index)
E                   removeFirst() //删除头结点 
E                   removeLast()  //删除尾节点
boolean             remove(Object o) //按元素删除
boolean             removeFirstOccurrence(Object o)
boolean             removeLastOccurrence(Object o)
void                clean()
----------------查找节点的方法----------------------
int                 indexOf(Object o)
int                 lastIndexOf(Object o)
E                   getFirst()    //查头结点
E                   getLast()     //查尾节点
E                   get(int index)  
boolean             contains(Object o)
int                 size()
----------------修改节点的方法----------------------
E                   set(int index,E element)
----------------LinkedList作为栈的方法--------------
void                push(E e)
E                   pop()
---------------LinkedList作为双端队列的方法---------
boolean             offerFirst(E e)
boolean             offerLast(E e)
E                   pollFirst()
E                   pollLast()
E                   peekFirst()
E                   peekLast()
---------------- 作为单向队列的方法-----------------
boolean             offer(E e) //等价于add
E                   poll()
E                   remove() //遇到队头为null,返回null
E                   peek()
E                   element() // 遇到对头为null,抛异常
---------------迭代器------------------------------
ListIterator<E>     listIterator(int index)
Iterator<E>         descendingIterator()
----------------LinkedList数组化------------------
Object[]            toArray()
<T> T[]             toArray(T[] a)
----------------其他方法--------------------------
Object              clone() //LinkedList浅拷贝
Spliterator<E>      spliterator()

直接继承超类或来自实现接口的default方法

public                   int hashCode() //继承自AbstractList
public                   boolean equals(Object o) //继承自AbstractList
public List<E>           subList(int fromIndex, int toIndex)//继承自AbstractList
default                  Stream<E> stream()//来自Colletion接口 
default                  Stream<E> parallelStream() //来自Colletion接口 
default boolean          removeIf(Predicate<? super E> filter) //来自接口Collection
public boolean           isEmpty() //继承自AbstractCollection
public boolean           retainAll(Collection<?> c) //继承自AbstractColletion
public Iterator<E>       iterator() //继承自AbstractSequentialList
default void             sort(Comparator<? super E> c) //来自List接口
default void             replaceAll(UnaryOperator<E> operator) //来自List接口
default void             replaceAll(UnaryOperator<E> operator) //来自List接口

LinkedList类的包方法

void            linkLast(E e)
void            linkBefore(E e,Node<E> succ)
E               unlink(Node<E> x)
Node<E>         node(int index)

LinkedList类的private方法

void            linkFist(E e)
E               unlinkFirst(Node<E> f)
E               unlinkLast(Node<E> l)
boolean         isElementIndex(int index)
boolean         isPositionIndex(int index)
String          outOfBounsMsg(int index)
void            checkElementIndex(int index)
void            checkPositionIndex(int index)
LinkedList<E>   superClone()
void            writeObject(java.io.ObjectOutputStream s)
void            readObject(java.io.ObjectInputStream s)

5. LinkedList源码(基于jdk1.8.0_231)

package java.util;
import java.util.function.Consumer;

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0; //双端链表的实际含元素
    transient Node<E> first; //头结点的引用
    transient Node<E> last;  //尾结点的引用

   //构造一个空的LinkedList
    public LinkedList() {
    }
    //构造一个LinkedList包含具体的集合,如果集合为null,会抛出NullPointerException
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    //头插法,即每次添加的元素都为第一个元素,私有的类方法
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);  // 
        first = newNode; //更新first指向newNode,即newNode为头结点了
        if (f == null)    //f为空,即初始的链表为空,让first,last同时指向newNode
            last = newNode;
        else
            f.prev = newNode; //否则f不为空,让f.pre指向newNode
        size++;
        modCount++;
    }
   //尾插法,即每次添加元素都添加在链表的最后一个后面,包方法
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode; //更新最后一个节点为newNode
        if (l == null)   //如果l为空,说明链表是空链表
            first = newNode;  //让first,last同时指向newNode
        else            
            l.next = newNode; //否则让l.next指向newNode
        size++;
        modCount++;
    }
    //按元素插入,在节点succ前插入e节点,succ不能为null节点,注意null节点与节点的item=null不是一个概念,可以在item=null前插入新节点哦
    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;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
    //删除头节点frist,在将LinkedList作为队列,双端队列,弹出头节点时使用,private方法被poll removeFirst pollFirst等public方法调用
    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 GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
    //删除尾节点last,在将LinkedList作为队列,双端队列,弹出尾节点时使用或栈式,弹出栈顶,private方法被removeLast pollLast等public方法调用  
    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 GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    //把节点x前后断开,把x节点从链表中移除
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next; //x节点前节点
        final Node<E> prev = x.prev; //x节点后节点

        if (prev == null) { //前节点为空,说明x节点就是first指向的节点,就是第一个节点
            first = next; 
        } else {
            prev.next = next; //x前节点不为空, 让前节点next域指向next节点
            x.prev = null;    //将x节点prev域置空
        }

        if (next == null) {  //x后节点为空,说明x节点就是last指向的节点,就是最后一个节点
            last = prev;  
        } else {               
            next.prev = prev; //x后节点prev域指向x前节点
            x.next = null;    //x节点的next域置空
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }
    //返回第一个节点item域
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
    //返回最后一个节点item域
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
    //remove第一个节点
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    //remove最后一个节点
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
    //头插法加入一个节点,每次节点都成为第一个节点,first更新指向新加入的节点
    public void addFirst(E e) {
        linkFirst(e);
    }
   //尾插法加入一个节点,每次节点都成为最后一个节点,last更新指向新加入的节点
    public void addLast(E e) {
        linkLast(e);
    }
    //检查链表是否至少存在一个对象o,若存在,返回true,时间复杂度为O(n)
    public boolean contains(Object o) {
        return indexOf(o) != -1; //indexOf遍历链表,得出o的位置,若o不在链表中,返回-1,否则返回位置索引
    }
    //返回链表种总共有多少个元素
    public int size() {
        return size;
    }
    //尾插法,加入新元素,时间复杂度为O(1),此方法等价于addLast
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    //remove链表中顺序第一次出现的指定的对象o,移除成功会返回true,否则返回false
    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;
    }
    //从链表尾部新加入一个集合,c为null,会抛NullPointerException
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
    //从具体位置新加入一个新集合
    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;
    }
    //清空链表
    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }
    //根据位置,返回位置上的节点item域,其时间复杂度为O(n),千万不要再迭代for循环中用来取元素,这可是线上生产级别的重大事故,炒鱿鱼丢饭碗级别的事故
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    //将index位置的元素替换为element,并且返回原节点的item域,同get一样,千万不要在for中迭代中使用set
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }
    //按位置插入,在index中插入elemet,index位置有元素的话,element就插在其前
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    //按位置删除,删除第index位置的节点,返回删除节点的item域
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    //检查index在不在合理的链表范围,如果size在[0,size)返回true,否则返回false
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    //检查index的范围,在add操作情况下,index=size也是合理的
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    //越界异常信息提示
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }
    
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    //返回index位置的节点,其中这里做个优化,如果index小于size的一半,从first指向的节点(头节点)往后找,如果index大于size一半,从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;
        }
    }

    // Search Operations
    //从头结点前往后,返回对象o在链表中首次出现的位置
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }
    ////从尾结点开始,从后往前,返回对象o在链表中首次出现的位置
    public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (x.item == null)
                    return index;
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (o.equals(x.item))
                    return index;
            }
        }
        return -1;
    }
      
    // Queue operations.

    //peek是返回头结点的item域,如果头结点为null,则返回null
    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }
    //返回头结点的item域,但是头结点=null时,会抛NoSuchElementException
    public E element() {
        return getFirst();
    }

    //返回头结点,且删除头结点,如果头结点=null,也返回null 
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    //返回头结点,且删除头结点,如果头结点=null,会抛出NoSuchElementException
    public E remove() {
        return removeFirst();
    }

    //加入一个元素到链表尾部
    public boolean offer(E e) {
        return add(e);
    }

    // Deque operations 因为LinkedList实现了Deque接口,必然需要提供双端队列该有“服务”,可前后入队和出队
   //从头入队
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    //从尾入队
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    //返回头节点item域,头节点=null 也返回,不抛异常
    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
     }


    //返回尾节点,尾节点=null,页返回,不抛异常
    public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }

    //返回并删除头节点,头结点=null,也返回,不抛异常
    public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    //返回并删除尾节点,尾结点=null,也返回,不抛异常
    public E pollLast() {
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }
     

    //下面push pop 将LinkedList作为栈,链表头始终作为栈顶,
    //将LinkedList作为栈,头作为栈顶,压入一个元素入栈
    public void push(E e) {
        addFirst(e);
    }

    //将LinkedList作为栈,头作为栈顶,将一个个元素弹出栈
    public E pop() {
        return removeFirst();
    }
   //从头结点往后,remove链表第一次出现的对象o,remove成功返回true,否则返回false
    public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

    //从尾结点往后,remove链表第一次出现的对象o,remove成功返回true,否则返回false
    public boolean removeLastOccurrence(Object o) {
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    //listIterator是双端迭代器,即可以从前也可以往后,也可以从特定位置开始迭代,迭代过程运行 fail-fast机制,通过迭代器的增删本质上就是对原链表的操作,
    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    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;
            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();
        }
    }
    //每个节点的数据结构
    private static class Node<E> {
        E item;  //item域
        Node<E> next; //next域
        Node<E> prev;  //prev域

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    /**
     * @since 1.6
     */
   //从尾结点开始往前遍历节点
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }

    /**
     * Adapter to provide descending iterators via ListItr.previous
     */
    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();
        }
    }

    @SuppressWarnings("unchecked")
   //浅拷贝LinkedList
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    //返回LinkedList实例的一个浅拷贝,言下之意为:item域如果为引用类型,拷贝的只是指向内容的引用,而不是内存内容,若item是基本类型,可以认为就是深拷贝了
    public Object clone() {
        LinkedList<E> clone = superClone();

        // Put clone into "virgin" state
        clone.first = clone.last = null;
        clone.size = 0;
        clone.modCount = 0;
        // Initialize clone with our elements
        for (Node<E> x = first; x != null; x = x.next)
            clone.add(x.item);
        return clone;
    }

    //
    public Object[] toArray() {
        Object[] result = new Object[size];
        int i = 0;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }

    // 
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            a = (T[])java.lang.reflect.Array.newInstance(
                                a.getClass().getComponentType(), size);
        int i = 0;
        Object[] result = a;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;

        if (a.length > size)
            a[size] = null;

        return a;
    }

    private static final long serialVersionUID = 876323262645176354L;
    
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out size
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (Node<E> x = first; x != null; x = x.next)
            s.writeObject(x.item);
    }

    @SuppressWarnings("unchecked")
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read in size
        int size = s.readInt();

        // Read in all elements in the proper order.
        for (int i = 0; i < size; i++)
            linkLast((E)s.readObject());
    }

    @Override
    public Spliterator<E> spliterator() {
        return new LLSpliterator<E>(this, -1, 0);
    }

    /** A customized variant of Spliterators.IteratorSpliterator */
    static final class LLSpliterator<E> implements Spliterator<E> {
        static final int BATCH_UNIT = 1 << 10;  // batch array size increment
        static final int MAX_BATCH = 1 << 25;  // max batch array size;
        final LinkedList<E> list; // null OK unless traversed
        Node<E> current;      // current node; null until initialized
        int est;              // size estimate; -1 until first needed
        int expectedModCount; // initialized when est set
        int batch;            // batch size for splits

        LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {
            this.list = list;
            this.est = est;
            this.expectedModCount = expectedModCount;
        }

        final int getEst() {
            int s; // force initialization
            final LinkedList<E> lst;
            if ((s = est) < 0) {
                if ((lst = list) == null)
                    s = est = 0;
                else {
                    expectedModCount = lst.modCount;
                    current = lst.first;
                    s = est = lst.size;
                }
            }
            return s;
        }

        public long estimateSize() { return (long) getEst(); }

        public Spliterator<E> trySplit() {
            Node<E> p;
            int s = getEst();
            if (s > 1 && (p = current) != null) {
                int n = batch + BATCH_UNIT;
                if (n > s)
                    n = s;
                if (n > MAX_BATCH)
                    n = MAX_BATCH;
                Object[] a = new Object[n];
                int j = 0;
                do { a[j++] = p.item; } while ((p = p.next) != null && j < n);
                current = p;
                batch = j;
                est = s - j;
                return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);
            }
            return null;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Node<E> p; int n;
            if (action == null) throw new NullPointerException();
            if ((n = getEst()) > 0 && (p = current) != null) {
                current = null;
                est = 0;
                do {
                    E e = p.item;
                    p = p.next;
                    action.accept(e);
                } while (p != null && --n > 0);
            }
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

        public boolean tryAdvance(Consumer<? super E> action) {
            Node<E> p;
            if (action == null) throw new NullPointerException();
            if (getEst() > 0 && (p = current) != null) {
                --est;
                E e = p.item;
                current = p.next;
                action.accept(e);
                if (list.modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                return true;
            }
            return false;
        }

        public int characteristics() {
            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
        }
    }
}

API总结(一)

LinkedList由于数据结构的灵活性,设计了大量API,一些增删改查的API功能很相似。这里我们仅仅讨论public方法,下面给出对比:

  • 增加节点的API
-----------------------------------------双向链表中的增加节点方法------------------------------------------------------------
public void addFirst(E e) //增加节点成为头节点,头插法,函数内部调用私有方法linkFirst(E e) 完成头插
public void addLast(E e)  //增加节点称为尾节点,尾插法,函数内部调用私有方法linkLast(E e) 完成尾插

----------------------------------------单向链表中增加节点的方法-------------------------------------------------------------
public boolean add(E e)  //尾插法,增加一个新结点,返回true,函数内部调用私有方法linkLast(E e)完成尾插
public void add(int index, E element)  //在指定位置新增一个新节点,只适用LinkedLinked为单双向链表的,不符合作为队列,栈

----------------------------------------addAll方法用在单双链表中都可---------------------------------------------------------
public boolean addAll(Collection<? extends E> c) // 加入一个集合的元素,LinkedList作为单向链表,双向链表,单向队列,双向队列使用此方法都合适,作为栈的时候,不合适
public boolean addAll(int index, Collection<? extends E> c) //从指定位置加入一个集合的元素,LinkedList作为单向链表,双向链表,单向队列,双向队列使用此方法都合适,作为栈的时候,不合适

-----------------------------------------单向队列中增加节点的方法---------------------------------------------------------
public boolean offer(E e) //等价于add(E e)方法,offer内部也是调用add方法的,实现尾插

-----------------------------------------双向队列中增加节点的方法---------------------------------------------------------
public boolean offerFirst(E e)  //offerFist函数内部调用addFirst(E e),始终返回true,完成头插,等价于addFirst
public boolean offerLast(E e)  // offerLast函数内部调用addLast(E e),始终返回true,完成尾插,等价于addFirst

----------------------------------------栈中增加节点的方法---------------------------------------------------------------
public void push(E e)  //将元素压入栈顶,LinkedList始终将头节点作为栈顶,push函数内部调用addFist函数完成操作,等价于addFist函数

  • 删除节点的API
public E removeFirst()  // 移除头结点,如果头节点为null,抛出NoSuchElementEXception,函数内部调用私有方法unlinkFirst(Node<E> f)完成头移除
public E removeLast()   // 移除尾结点,如果尾节点为null,抛出NoSuchElementEXception,函数内部调用私用方法unlinkLast(Node<E> l)完成尾移除
public boolean remove(Object o)  // 移除指定的节点o,节点o可以为null,若节点o存在在链表中,移除成果,返回true,否则返回false
public E remove(int index)  //移除指定位置的元素,会检查index的范围[0,size)
public void clear()  //清空LinkedList
-------------------------------------LinkedList作为单向队列时的删除方法-----------------------------------------------------------------
public E poll()  //检索和删除对头节点,若对头为空(即队列为空),返回null,不为空,返回头节点的item域,poll函数内部调用私有unlinkFirst(Node<E> f)方法
public E remove() // 等价于removeFist函数,移除对头节点,如果头节点为null,抛出NoSuchElementEXception

-----------------------------------------双向队列中删除节点的方法-------------------------------------------------------------
public E pollFirst()  //移除双端队列的队头节点,若队头节点为null,返回null,若不为空,移除对头,返回对头节点的item域
public E pollLast()   //移除双端队列的队尾节点,若队尾节点为null,返回null,若不为空,移除对尾,返回对尾节点的item域

----------------------------------------栈中删除节点的方法---------------------------------------------------------------
public E pop()   //弹出栈顶的元素,函数内部调用removeFirst()完成操作,等价于removeFirst
  • 查询节点的API
-----------------------------------------单双链表中的查询节点方法------------------------------------------------------------
public E getFirst() // 得到头结点的item域,如果头结点为null,抛出NoSuchElementException
public E getLast()  // 得到尾结点的item域,如果尾结点为null,抛出NoSuchElementException
public E get(int index)  //得到指定index位置的元素,会检查index的范围[0,size)
public int indexOf(Object o)     //顺序(从头结点往尾节点)查找节点o第一次出现在LinkedList中的位置
public int lastIndexOf(Object o) //逆序(从尾结点往头结点)查找节点o第一次出现在LinkedList中的位置
----------------------------------------单项队列中查询节点的方法-------------------------------------------------------------
public E peek()  //返回队列的第一个节点,如队列为空,返回null,队列不为空,返回对头节点的item域,peek函数只负责检索,不会删除对头节点
public E element() //返回队列的第一个节点,如队列为空,抛出NoSuchElementException,函数内部调用getFist()函数,两者是等价的.element函数只负责检索,不会删除对头节点

-----------------------------------------双向队列中查询节点的方法---------------------------------------------------------
public E peekFirst()  // 返回双端队列的队头,如果对头为null,返回null,如果队头不为空,返回队头节点的item域
public E peekLast()   // 返回双端队列的队尾,如果对尾为null,返回null,如果队尾不为空,返回队尾节点的item域
  • 更新节点的API
public E set(int index, E element)  //将index位置的元素更新为element

6. LinkedList示例

作为双向链表使用

public class LinkedListTest {
    public static void main(String[] args) {
        List<Integer> arr = new Random(32)
                .ints(0,1000).boxed().limit(20).collect(Collectors.toList());
        System.out.println(arr);
        /**
         * 创建
         */
        LinkedList<Integer> myLinkedList = new LinkedList<>(arr);

        /**
         * 作为双端列表 getFirst getLast removeFirst removeLast addFirst addLast set removeIf replaceAll
         * indexOf lastIndexOf
         */
        System.out.println(myLinkedList.getFirst());
        System.out.println(myLinkedList.getLast());
        myLinkedList.removeFirst();
        myLinkedList.removeLast();
        System.out.println(myLinkedList);
        myLinkedList.addFirst(377); //头插
        myLinkedList.addLast(644);  //尾插
        System.out.println(myLinkedList);
        myLinkedList.set(myLinkedList.size() - 1,-1);
        System.out.println(myLinkedList.get(myLinkedList.size()-1));
        System.out.println(myLinkedList);
        System.out.println(myLinkedList.set(myLinkedList.size()-1, 644));// 注意 get set 本身操作都是O(n)
        System.out.println(myLinkedList);
        myLinkedList.addAll(Arrays.asList(-1,-2,-3,-4));
        System.out.println(myLinkedList);
        myLinkedList.removeIf(x->x<=0);
        System.out.println(myLinkedList);
        myLinkedList.addAll(Arrays.asList(0,0,0,0));
        System.out.println(myLinkedList);
        myLinkedList.replaceAll(x -> {
            if(x == 0) x =100;
            return x;
        });
        System.out.println(myLinkedList);
        myLinkedList.removeAll(Arrays.asList(100));
        System.out.println(myLinkedList);
        System.out.println(myLinkedList.indexOf(122));
        System.out.println(myLinkedList.lastIndexOf(122));

        /**
         * 迭代操作
         */

        System.out.println(myLinkedList);
        ListIterator<Integer> listIterator = myLinkedList.listIterator();
        listIterator.forEachRemaining(x-> System.out.print(x+","));
        System.out.println();
        myLinkedList.forEach(x-> System.out.print(x+","));
        System.out.println();
        Iterator<Integer> iterator = myLinkedList.descendingIterator();
        iterator.forEachRemaining(x-> System.out.print(x+","));
        System.out.println();
        Iterator<Integer> ite = myLinkedList.iterator();
        ite.forEachRemaining(x-> System.out.print(x+","));
        System.out.println();
        int size = myLinkedList.size();
        for(int i =0;i<size;i++){
            System.out.print(myLinkedList.get(i)+","); //不要这样做,这个时间复杂度是O(n*n)
        }

    }
}

  • 结果
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
377
644
[331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
-1
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, -1]
-1
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, -1, -2, -3, -4]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, 0, 0, 0, 0]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644, 100, 100, 100, 100]
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
5
5
[377, 331, 985, 41, 239, 122, 200, 791, 256, 734, 937, 80, 806, 346, 676, 748, 406, 92, 611, 644]
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
644,611,92,406,748,676,346,806,80,937,734,256,791,200,122,239,41,985,331,377,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,
377,331,985,41,239,122,200,791,256,734,937,80,806,346,676,748,406,92,611,644,

作为栈使用

练习使用push pop函数,LinkedList作为栈,头节点始终为栈顶

public class MyStack<E> extends LinkedList<E> {
    public void push(E e){
        super.push(e);
    }
    public E pop(){
        E e = super.pop();
        return e;
    }
    public static void main(String[] args) {
        MyStack<String> stack = new MyStack<>();
        stack.push("hello");
        stack.push("world");
        System.out.println(stack.toString());
        String s = stack.pop();
        System.out.println(s);
        System.out.println(stack);
    }
}
  • 结果
[world, hello]
world
[hello]

作为双端队列使用

public class MyQueue<E> extends LinkedList<E> {
    public MyQueue(Collection<? extends E> c) {
        super.addAll(c);
    }
    /**
     * 对头的增删查
     *
     */
    //peekFirst是返回头结点的item域,如果头结点为null,则返回null
    @Override
    public E peekFirst() {
        return super.peekFirst();
    }

    //从队头入队
    @Override
    public boolean offerFirst(E e) {
        return super.offerFirst(e);
    }
    //返回头结点,且删除头结点,如果头结点=null,也返回null

    @Override
    public E pollFirst() {
        return super.pollFirst();
    }
    /**
     * 队尾的增删查
     */
    @Override
    public boolean offerLast(E e) {
        return super.offerLast(e);
    }
    @Override
    public E removeLast() {
        return super.removeLast();
    }
    @Override
    public E peekLast() {
        return super.peekLast();
    }
    public static void main(String[] args) {
        MyQueue<Integer> myQueue = new MyQueue<>(Arrays.asList(20,13,15));
        System.out.println(myQueue);
        myQueue.offerLast(100);
        System.out.println(myQueue); //加入队尾
        myQueue.offerFirst(1);
        System.out.println(myQueue); //加入队头
        System.out.println(myQueue.peekFirst());
        System.out.println(myQueue.peekLast());
        myQueue.offerLast(null);
        System.out.println(myQueue.peekLast()); //返回null
        myQueue.pollFirst();
        System.out.println(myQueue); //从对头出队
        myQueue.pollLast();
        System.out.println(myQueue);//从队尾出队
    }
}
  • 结果
[20, 13, 15]
[20, 13, 15, 100]
[1, 20, 13, 15, 100]
1
100
null
[20, 13, 15, 100, null]
[20, 13, 15, 100]

7. 面试session

  • 谈谈你对LinkeList的认识?
    LinkedList数据结构是双端链表,由于其数据结构的灵活性,LinkedList可以做单向链表,双向链表,单向队列,双端队列,栈(头结点所在位置始终为栈顶)。针对不同的数据结构,LinkedList给出了操作API.
    当LinkedList做单向链表时,我习惯使用add方法
    当LinkedList做双端链表时,我习惯使用getFirst getLast removeFirst removeLast addFirst addLast方法
    而 get set addALL 这些方法一般,在单双向链表中,我都会使用.
    当LinkedList做单向队列时,我习惯使用 offer remove() poll peek element方法,其中peek element都是返回队头,当peek遇到空队头时,return null,而element抛异常;
    当做双端队列时,我习惯使用peekFirst peekLast offerFirst offerLast pollFirst pollLast
    当做栈时,我习惯使用pop push方法
    一般地,peek返回值,不会删除;offer一般等价add;peek,poll在遇到null都会返回null,不会抛异常。

  • LinkedList和ArrayList的异同和使用场景?
    简单来讲,大量的删除 增加 适合LinkedList,大量的随机访问适合ArrayList.特别低,大量在头部的增加删除操作,特别适合LinkedList,大量中间位置的查找适合ArrayList.

  • LinkedList可以模拟哪些数据结构?
    单向链表 双端链表 栈 队列 双端队列

  • LinkedList有几种迭代方式?
    forEach listIterator() iterator() descendingIterator()

posted @ 2020-07-17 02:27  ahpuched  阅读(236)  评论(0编辑  收藏  举报