• LinkedList源码学习和总结


    概括

    LinkedList见名知意,底层由链表实现,内存不连续

    增删快,查改慢(相对而言),不支持随机访问(没有实现RandomAccess接口)

    非线程安全,用    Collections.synchronizedList(new LinkedList<>())

    类继承关系图

     既可以用作链表,也可以用作队列和双端队列,还可以用作栈

    复杂度

          get(int index)                 需要从first开始遍历,复杂度O(n)
          add(E e)                        添加到末尾,调用的linkLast(E e),复杂度O(1)
          add(index, E)                在第几个元素的位置添加新元素,调用的是linkList或linkBefore,复杂度O(n) 
          remove(int index)  和 remove(Object o)                 删除元素,复杂度O(n)

    源码

    类说明翻译

    /**
     * Doubly-linked list implementation of the {@code List} and {@code Deque} interfaces.  Implements all optional list operations, and permits all elements (including {@code null}).
     * 双向链表List(这里已经告诉你它的实现是 Doubly-linked) 实现了 List 和 Deque接口,实现了list 接口的所有操作,允许包括null在内的所有的值 
     * All of the operations perform as could be expected for a doubly-linked list.  Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.
     * 它的所有操作都符合对双向链表的预期 ,关于index(下标,或者是索引)的操作都会从头或者从尾部遍历整个链表,直到遍历到这个下标
     * Note that this implementation is not synchronized 
     * If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally.  
    * (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.)
    * This is typically accomplished by synchronizing on some object that naturally encapsulates the list. * 上面这一段是说它不是线程安全的,如果多个线程并发访问一个链表,并且至少一个线程要做结构性修改(结构性修改是指增删一个或多个元素的操作,仅仅对一个元素的值做修改不是结构性更改),那么需要在对它操作之前,增加同步手段来控制对它的访问,比如对象加锁的方式 * If no such object exists, the list should be "wrapped" using the {
    @link Collections#synchronizedList Collections.synchronizedList} method.
    * This is best done at creation time, to prevent accidental unsynchronized access to the list:
    * <pre> List list = Collections.synchronizedList(new LinkedList(...));</pre> * 如果没有这样的可加锁的对象,可以使用LinkedList 的包装类对象,也就是调用List list = Collections.synchronizedList(new LinkedList(...))来创建包装类对象来创建一个线程安全的链表 * <p>The iterators returned by this class's {
    @code iterator} and {@code listIterator} methods are <i>fail-fast</i>:
    * if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator's own {
    @code remove} or {@code add} methods,
    * the iterator will throw a {
    @link ConcurrentModificationException}. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than * risking arbitrary, non-deterministic behavior at an undetermined time in the future. * 这一段讲的是快速失败的特性,以及如何避免,和ArrayList相似,下面是说快速失败机制不应该用来设计程序编码,只是用来排查bug的 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> * @author Josh Bloch * @see List * @see ArrayList * @since 1.2 * @param <E> the type of elements held in this collection */

    主要属性

     transient int size = 0;
    
        /**
         * Pointer to first node.       ----------first的prev一定是null
         * Invariant: (first == null && last == null) ||      ------就是说first==null的话,那么last一定也是null,因为first==null,则这个链表就是一个空链表,没任何元素;所以只要出现if(first==null)的话,一定要给last赋同一个新node值
         *            (first.prev == null && first.item != null)------  如果first的prev为null,说明first一定是有元素的,item一定不为null
         */
        transient Node<E> first;
    
        /**
         * Pointer to last node.        ----------last的next一定是null
         * 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;
            }
      }

    几个多次用到的公共方法

    • 返回指定索引位置的Node

    这个方法在源码多个位置被调用到,而且充分利用到了双向链表的特性,根据index和size/2的大小关系决定从前还是从后遍历

       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;
            }
        }

    作为队列和栈时的主要方法

    Queue操作:
    E peek()     返回head(就是first.item).相似的一个方法是E element(),head不存在时直接抛异常,所以这个方法可以忽略
    E poll() 删除并返回head。E remove()相同,可能抛出异常,所以忽略
    boolean offer(E e),调用的是add(),添加元素作为tail(也就是last)
    
    Dequeue操作
    E peekFirst(),E peekLast()
    E pollFirst(),E pollLast()
    boolean offerFirst(E e),boolean  offerLast(E e)
    
    Stack操作
    void push(E e)   调用的是addFirst(e)
    E pop() 

     遍历的最佳方式

    前面复习ArrayList的时候已经讲过了,不支持随机访问的情况下,当然是迭代器遍历最快了,实验数据很简单

    和ArrayList的对比

    实现原理,性能(从存和取,结构修改三个角度)

  • 相关阅读:
    leetcode 68 Text Justification
    了解HTTP协议
    对编码的一点理解
    极简WebSocket聊天室
    统一响应数据的封装
    BitMap
    SPI机制
    Holder类
    Java的标签
    二叉树的非递归遍历
  • 原文地址:https://www.cnblogs.com/yb38156/p/16467014.html
Copyright © 2020-2023  润新知