• Java集合--LinkedList


    LinkedList 的本质是双向链表。实现 List 接口,能对它进行队列操作。实现 Deque 接口,能将LinkedList当作双端队列使用。
    LinkedList 是非同步的。

    LinkedList的继承关系

    java.lang.Object
       ↳     java.util.AbstractCollection<E>
             ↳     java.util.AbstractList<E>
                   ↳     java.util.AbstractSequentialList<E>
                         ↳     java.util.LinkedList<E>
    
    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}

    LinkedList构造函数

    // 默认构造函数
    LinkedList()
    // 创建一个LinkedList,保护Collection中的全部元素。
    LinkedList(Collection<? extends E> collection)

    LinkedList的API 

    boolean       add(E object)
    void          add(int location, E object)
    boolean       addAll(Collection<? extends E> collection)
    boolean       addAll(int location, Collection<? extends E> collection)
    void          addFirst(E object)
    void          addLast(E object)
    void          clear()
    Object        clone()
    boolean       contains(Object object)
    Iterator<E>   descendingIterator()
    E             element()
    E             get(int location)
    E             getFirst()
    E             getLast()
    int           indexOf(Object object)
    int           lastIndexOf(Object object)
    ListIterator<E>     listIterator(int location)
    boolean       offer(E o)
    boolean       offerFirst(E e)
    boolean       offerLast(E e)
    E             peek()
    E             peekFirst()
    E             peekLast()
    E             poll()
    E             pollFirst()
    E             pollLast()
    E             pop()
    void          push(E e)
    E             remove()
    E             remove(int location)
    boolean       remove(Object object)
    E             removeFirst()
    boolean       removeFirstOccurrence(Object o)
    E             removeLast()
    boolean       removeLastOccurrence(Object o)
    E             set(int location, E object)
    int           size()
    <T> T[]       toArray(T[] contents)
    Object[]     toArray()

    源码分析

    List接口的实现类之一ArrayList的内部实现是一个数组,而另外一个实现LinkedList内部实现是使用双向链表。

    LinkedList在内部定义了一个叫做Node类型的内部类,这个Node就是一个节点,链表中的节点,这个节点有3个属性,分别是元素item(当前节点要表示的值), 前节点prev(当前节点之前位置上的一个节点),后节点next(当前节点后面位置的一个节点)。

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

    LinkedList的3个属性:

    transient int size = 0; // 集合链表内节点数量
    transient Node<E> first; // 集合链表的首节点
    transient Node<E> last; // 集合链表的尾节点

    add(E e)

    添加元素到链表的最后一个位置

    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null); // 由于是添加元素的链表尾部,所以也就是这个新的节点是最后1个节点,它的前节点肯定是目前链表的尾节点,它的后节点为null
        last = newNode; // 尾节点变成新的节点
        if (l == null) // 如果一开始尾节点还没设置,那么说明这个新的节点是第一个节点,那么首节点也就是这个第一个节点
            first = newNode;
        else // 否则,说明新节点不是第一个节点,处理节点前后关系
            l.next = newNode;
        size++; // 节点数量+1
        modCount++;
    }

    add(int index, E element)

    添加元素到列表中的指定位置

    public void add(int index, E element) {
        checkPositionIndex(index); // 检查索引的合法性,不能超过链表个数,不能小于0
    
        if (index == size) // 如果是在链表尾部插入节点,那么直接调用linkLast方法,上面已经分析过
            linkLast(element);
        else // 不在链表尾部插入节点的话,调用linkBefore方法,参数为要插入的元素值和节点对象
            linkBefore(element, node(index));
    }

    根据索引找到对应的节点

    Node<E> node(int index) {
        // 用了一个小算法,如果索引比链表数量的一半还要小,从前往后找,这样只需要花O(n/2)的时间获取节点
        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;
        }
    }
    
    void linkBefore(E e, Node<E> succ) { // succ节点表示要新插入节点应该在的位置
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ); // 1:新节点的前节点就是succ节点的前节点,新节点的后节点是succ节点
        succ.prev = newNode; // 2:succ的前节点就是新节点
        if (pred == null)    // prev=null表示succ节点就是head首节点,这样的话只需要重新set一下首节点即可,首节点的后节点在步骤1以及设置过了
            first = newNode;
        else // succ不是首节点的话执行步骤3
            pred.next = newNode; // 3:succ节点的前节点的后节点就是新节点
        size++; // 节点数量+1
        modCount++;
    } 

    上面代码中注释的1,2,3点就在下图中表示

    LinkedList还提供了2种特殊的add方法,分别是addFirst和addLast方法,处理添加首节点和尾节点。

    remove(int index)

    移除指定位置上的节点

    public E remove(int index) {
        checkElementIndex(index); // 检查索引的合法性,不能超过链表个数,不能小于0 
        return unlink(node(index));
    }
    
    E unlink(Node<E> x) {
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
    
        if (prev == null) { // 特殊情况,删除的是头节点
            first = next;
        } else {
            prev.next = next; // 1
            x.prev = null; // 1
        }
    
        if (next == null) { // 特殊情况,删除的是尾节点
            last = prev;
        } else {
            next.prev = prev; // 2
            x.next = null; // 2
        }
    
        x.item = null; // 3
        size--; // 链表数量减一
        modCount++;
        return element;
    }

    上面代码中注释的1,2,3点就在下图中表示

    get(int index)

    得到索引位置上的元素

    public E get(int index) {
        checkElementIndex(index); // 检查索引的合法性,不能超过链表个数,不能小于0 
        return node(index).item; // 直接找到节点,返回节点的元素值即可
    }
  • 相关阅读:
    java 的 线程池应用和实践
    拦截信息短信息并转发到指定手机
    分享 UC优视 的android程序员面试题
    解释通讯协议中的xml
    设计模式工厂模式
    MongoDB基础教程系列第一篇 进入MongoDB世界
    Docx组件读写Word文档介绍
    [转]Oracle数据库逻辑增量备份之exp/imp
    JSON文件读取
    JAVA综合--如何掌握JDK1.5枚举类型[转]
  • 原文地址:https://www.cnblogs.com/hesier/p/5802383.html
Copyright © 2020-2023  润新知