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
是一个线程不安全的类。
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18177158
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现