zoukankan      html  css  js  c++  java
  • 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()

  • 相关阅读:
    水晶苍蝇拍:微薄投资感悟摘录(四) (2012-04-03 14:11:01)
    水晶苍蝇拍:投资感悟(三)(手打,有删减)
    水晶苍蝇拍:投资感悟(二)(2011-12-27 08:17:54)
    leetcode -- String to Integer (atoi)
    leetcode -- Longest Palindromic Substring
    leetcode -- Longest Substring Without Repeating Characters
    leetcode -- Add Two Numbers
    QQ截图工具截取
    LUA学习笔记(第5-6章)
    Lua5.2 请求 luasocket 相关模块时的 multiple-lua-vms-detected
  • 原文地址:https://www.cnblogs.com/ahpucd/p/13326879.html
Copyright © 2011-2022 走看看