源码学习(2) LinkedList源码分析

342 阅读3分钟

代码版本: JDK 1.8

都是集合框架,和上篇文章思路一样。

介绍: LinkedList底层是基于双向链表实现的。不需扩容

  1. LinkedList实例化(啥也没有)
public LinkedList() {}
  1. 增加元素 首先看一下,我们增加一个元素,LinkedList底层到底是包装成了啥
/** * 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;
    }
}

正式看增加逻辑 --由于不需要扩容了,代码相对简单一些,修改下next prev的指向就ok。

public boolean add(E e) {
    linkLast(e); // 👇
    return true;
}
/**- 见名知意 增加的时候是在尾部添加节点 -*/
void linkLast(E e) {
    final Node<E> l = last; // 最后一个节点
    // 增加节点是在尾部,所以,每次都把last节点当做新节点的前驱prev,next为null,
    final Node<E> newNode = new Node<>(l, e, null);
    // 更新该LinkedList集合的last属性引用
    last = newNode;
    // 增加第一个元素时调用
    if (l == null)
        first = newNode;
    else
        l.next = newNode; // 上次增加元素时的最后一个节点的next修改为newNode
    size++; // 集合大小+1
    modCount++;
}
  1. 查询 --LinkedList查询比较费劲(并不是说代码难搞),使用了二分法
public E get(int index) {
    //  -_- 如果if后只跟一句话, 源码都不喜欢if加大括号
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    return node(index).item;// 👇
}
Node<E> node(int 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;
    }
}
  1. 删除
public E remove(int index) {
    Node<E> node = node(index); // 查询到要删除的节点
    return unlink(node); // 👇
}
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; // 前节点的next改为next节点
        x.prev = null; // 删除的节点前驱置空
    }
    // 删的是最后一个节点
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev; // 下一节点的prev改为prev节点
        x.next = null; // 删除的节点后驱置空
    }
    x.item = null; // 删除元素置空
    size--; // size-1
    modCount++;
    return element;
}

5.修改

public E set(int index, E element) {
    Node<E> x = node(index); // 获取元素
    E oldVal = x.item; 
    x.item = element; // 👈 偷梁换柱一下就ok了
    return oldVal;
}
  1. 指定位置添加元素
public void add(int index, E element) {
    if (index == size) // 嘿;-),这么巧。 增加的地方恰巧是最后一个元素
        linkLast(element); 
    else  // 几天没洗脸了,你不如上面的兄弟脸白啊!
        Node<E> node = node(index); // 还是先查询
        linkBefore(element, node); // 👇
}
void linkBefore(E e, Node<E> succ) {
    final Node<E> pred = succ.prev; // 马上被挤跑的兄弟,深明大义,留下了前辈的地址
    // 新来的要上位了,被挤跑的元素得委屈一下,要跟这新来的屁股后面(表面笑嘻嘻,内心mmp)
    final Node<E> newNode = new Node<>(pred, e, succ); 
    succ.prev = newNode; // 被挤跑的兄弟重新认识前辈
    if (pred == null) // 第一名被抢喽!~让你不努力
        first = newNode; // 第一次来就抢个第一名,小伙子实力很强啊。说:是不是给LinkedList送礼了
    else
        pred.next = newNode; // pre大哥需要重新认识小弟了
    size++; // LinkedList家庭成员又壮大了~
    modCount++;
}

End!! 过完一遍源码 ,是不是觉得ArrayListLinkedList 太简单了,其实就是这么简单。

但这都是弟弟,只是基础而已,HashMap 在后面排队等的不耐烦了,下一篇就翻你的牌子了。