LinkedList源码解析

LinkedList

首先看看实现LinkedList的基本元素

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

add 方法

public boolean add(E e) {
    // 调用linkLast方法
    linkLast(e);
    return true;
}

linkLast

transient Node<E> first;// 起始节点,标记链表的第一个节点
transient Node<E> last;// 末尾节点,标记链表的最后一个节点

void linkLast(E e) {
    final Node<E> l = last;// 获取末尾节点
    // new一个新节点,并把last节点作为父节点,e为当前节点的对象,子节点为null
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;// 把新节点赋给last
    // 判断末尾节点为null吗
    if (l == null)
        // 若为null,证明这个链表目前无节点,则把新节点赋给起始节点,没有元素的链表,现在新增一个节点,自然这个节点是没有子节点的
        first = newNode;
    else
        // 若不为null,证明这个链表当前有节点,则把当前节点赋值给last节点的子节点
        l.next = newNode;
    size++;// 链表长度+1
    modCount++;//修改计数+1
}

remove 方法

public E remove(int index) {
    // 检查index下标是否越界
    checkElementIndex(index);
    return unlink(node(index));
}

checkElementIndex

private void checkElementIndex(int index) {
    // 检查index下标是否越界,越界则抛出异常
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

isElementIndex 检查下标是否越界

private boolean isElementIndex(int index) {
    // 下标大于0且小于链表长度返回true
    return index >= 0 && index < size;
}

node 按下标获取待删除的node节点

Node<E> node(int index) {
    // assert isElementIndex(index);
    // 下标是否处于链表前1/2的下标范围
    if (index < (size >> 1)) {
        // 获取起始节点
        Node<E> x = first;
        // 循环,目的是从起始节点一直往下找,找index次,找到待删除的节点并返回
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        // 下标处于链表的后1/2的下标范围
        // 获取末尾节点
        Node<E> x = last;
        // 从末尾节点往前找,找size-index次,找到待删除的节点并返回
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

unlink

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;//记录待删除节点的父节点
    // 处理子节点
    if (prev == null) {
        // 若父节点为空,证明移除的是第一个节点,那么把起始节点更改为上一步记录的子节点
        first = next;
    } else {
        // 若父节点不为空,证明移除的是中间节点,那么把上一步的子节点赋值给父节点的子节点,待删除的父节点置为null
        prev.next = next;
        x.prev = null;
    }
    // 处理父节点
    if (next == null) {
        // 若子节点为空,证明移除的是最后一个节点,那么把末尾节点更改为上一步记录的父节点
        last = prev;
    } else {
        // 若子节点不为空,证明移除的是中间节点,那么把上一步的父节点赋值给子节点的父节点,待删除的子节点置为null
        next.prev = prev;
        x.next = null;
    }
    // 走完上面两个if...else...,就已经完成了删除时对链表的重新连接
    x.item = null;// 将x节点存储的对象置为null,此时x节点是一个{item:null,prev:null,next:null}的对象
    size--;//链表长度-1
    modCount++;//修改计数+1
    return element;// 返回 待删除的子节点的对象
}

clear 方法

清除链表所有节点

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
    // 以起始节点开始,以x为null结束
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;// 记录下一个节点
        // 处理当前节点,属性都置为null
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;// 将下一节点赋值给x,供下一次循环使用
    }
    first = last = null;// 起始节点 和 末尾节点都置为null
    size = 0;// 链表长度置为0
    modCount++;// 修改计数+1
}

get 方法

public E get(int index) {
    // 检查index下标是否越界
    checkElementIndex(index);
    // 返回 待查找节点的item对象,也就是节点存储的对象
    return node(index).item;
}

可以看出LinkedList是一个双向链表结构,每个节点Node中都有指向父节点的prev和指向子节点的next,以及存储对象的item
在链表中查找对象的时候,并不是从头到尾进行查找的,而是把根据下标index所处链表的前半段还是后半段为依据进行查找:处于前半段
就从头开始往后找,处于后半段就从末尾往前找(我个人猜测这大概是为了降低查找的耗时吧)。

LinkedList的方法中并没有使用锁或其他处理多线程的方案,说明LinkedList是一个线程不安全的类。

posted @   勤匠  阅读(2)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示