LinkedList 源码阅读

前言:

1、LinkedList,双向链表,底层数据结构是链表

2、不是线程安全的

3、查询慢,需要遍历链表,添加和删除节点很快,只需移动节点就行

4、既可以当队列,也可以当链表

一些重要的常量和变量


/**
     * 链表大小
     */
    transient int size = 0;

    /**
     * 头节点.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * 尾节点.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
    
    /**
     * 静态节点类,双向链表所以有前后指针,LinkedList通过first和last引用分别指向链表的第一个和最后一 个元素。
     * 注意这里没有所谓的哑元,当链表为空的时候first和last都指向null。
     */
    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简单示意图:(图来自https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/3-LinkedList.md

一些重要的方法:

  1. add()方法,添加新的节点到链表尾部


/**
     * 添加新的节点到链表尾部
     */
    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);
        last = newNode;
        //如果尾节点为null,则链表为空,将新节点设为头节点
        if (l == null)
            first = newNode;
        //不然就将新节点设为原先尾节点的后面
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    
    /**
     * 在要求的 index 插入元素
     */
    public void add(int index, E element) {
        checkPositionIndex(index);
        // 如果要插入的位置是末尾的位置
        if (index == size)
            linkLast(element);
        // 其他位置
        else
            linkBefore(element, node(index));
    }

    /**
     * 返回该索引的节点
     */
    Node<E> node(int index) {
        // 因为链表双向的,可以从开始往后找,也可以从结尾往前找,
        // 具体朝那个方向找取决于条件index < (size >> 1),也即是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;
        }
    }

    /**
     * 插入元素
     */
    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;
        // 头结点为null
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

添加节点示意图:

  1. 删除节点


/**
     * 根据 index 删除节点
     */
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    
    // 检查该 index 是否存在,不存在抛出异常
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }  

    /**
     * 判断index是否在链表中
     */
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    /**
     * 删除节点
     */
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
         
        // 修改 pre 指针指向, 处理头指针为null的情况
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        
        // 修改 next 指针指向,特殊处理尾指针为null的情况
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        // GC
        x.item = null;
        size--;
        modCount++;
        return element;
    }

   /**
     * 根据值删除节点
     */
    public boolean remove(Object o) {
        if (o == null) {
            // 遍历节点信息,找到节点value为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;
    }

remove()方法示例图:

3、 set()


  /**
     * 替换该index的值
     */
    public E set(int index, E element) {
        // 检查是否存在该索引
        checkElementIndex(index);
        // 找到该节点
        Node<E> x = node(index);
        // 值替换
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

参考:

https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/8-PriorityQueue.md 大佬的集合源码分析,写的很nice,图都来自这上面的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值