【JDK源码】LinkedList

文章详细介绍了Java链表中节点的增删查操作,包括通过索引高效查询、批量增加策略,以及如何利用modCount跟踪修改。讨论了与ArrayList的区别,强调了链表操作的原理和性能优化。

//通过下标获取某个node 的时候,(增、查 ),会根据index处于前半段还是后半段 进行一个折半,以提升查询效率

if (index < (size >> 1)) {

Node x = first;

for (int i = 0; i < index; i++)

x = x.next;

return x;

} else {

Node x = last;

for (int i = size - 1; i > index; i–)

x = x.prev;

return x;

}

}

  • checkPositionIndex(index)

private void checkPositionIndex(int index) {

if (!isPositionIndex(index))

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

private boolean isPositionIndex(int index) {

return index >= 0 && index <= size; //插入时的检查,下标可以是size [0,size]

}

小结:

  • 链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作。对比ArrayList是通过System.arraycopy完成批量增加的

  • 通过下标获取某个node 的时候,会根据index处于前半段还是后半段 进行一个折半,以提升查询效率

add

添加单个元素

  • add(E e)

//在尾部插入一个节点: add

public boolean add(E e) {

linkLast(e);

return true;

}

  • linkLast(e)

//生成新节点 并插入到 链表尾部, 更新 last/first 节点。

void linkLast(E e) {

final Node l = last; //记录原尾部节点

final Node newNode = new Node<>(l, e, null);//以原尾部节点为新节点的前置节点

last = newNode;//更新尾部节点

if (l == null)//若原链表为空链表,需要额外更新头结点

first = newNode;

else//否则更新原尾节点的后置节点为现在的尾节点(新节点)

l.next = newNode;

//size和modCount自增。

size++;

modCount++;

}

添加单个元素并且带上位置

  • add(int index, E element)

//在指定下标,index处,插入一个节点

public void add(int index, E element) {

checkPositionIndex(index);//检查下标是否越界[0,size]

if (index == size)//在尾节点后插入

linkLast(element);

else//在中间插入

linkBefore(element, node(index));

}

  • linkBefore(element, node(index))

//在succ节点前,插入一个新节点e

void linkBefore(E e, Node succ) {

// assert succ != null;

//保存succ的前置节点

final Node pred = succ.prev;

//以前置和后置节点和元素值e 构建一个新节点

final Node newNode = new Node<>(pred, e, succ);

//新节点new是原节点succ的前置节点

succ.prev = newNode;

if (pred == null)//如果之前的前置节点是空,说明succ是原头结点。所以新节点是现在的头结点

first = newNode;

else//否则构建前置节点的后置节点为new

pred.next = newNode;

size++;

modCount++;

}

小结:

  • 增一定会修改modCount

remove

按照位置删除结点

  • remove(int index)

//删:remove目标节点

public E remove(int index) {

checkElementIndex(index);//检查是否越界 下标[0,size)

return unlink(node(index));//从链表上删除某节点

}

  • unlink(node(index))

//从链表上删除x节点

E unlink(Node x) {

// assert x != null;

final E element = x.item; //当前节点的元素值

final Node next = x.next; //当前节点的后置节点

final Node prev = x.prev;//当前节点的前置节点

if (prev == null) { //如果前置节点为空(说明当前节点原本是头结点)

first = next; //则头结点等于后置节点

} else {

prev.next = next;

x.prev = null; //将当前节点的 前置节点置空 将节点置空,方便GC

}

if (next == null) {//如果后置节点为空(说明当前节点原本是尾节点)

last = prev; //则 尾节点为前置节点

} else {

next.prev = prev;

x.next = null;//将当前节点的 后置节点置空 将节点置空,方便GC

}

x.item = null; //将当前元素值置空

size–; //修改数量

modCount++; //修改modCount

return element; //返回取出的元素值

}

  • checkElementIndex(index)

private void checkElementIndex(int index) {

if (!isElementIndex(index))

throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

}

//下标[0,size)

private boolean isElementIndex(int index) {

return index >= 0 && index < size;

}

直接删除某个结点

remove(Object o)

//因为要考虑 null元素,也是分情况遍历

public boolean remove(Object o) {

if (o == null) {//如果要删除的是null节点(从remove和add 里 可以看出,结点允许元素为null)

//遍历每个节点 对比

for (Node x = first; x != null; x = x.next) {

if (x.item == null) {

unlink(x);

return true;

}

}

} else {

for (Node x = first; x != null; x = x.next) {

if (o.equals(x.item)) {

unlink(x);

return true;

}

}

}

return false;

}

小结

  • 删也一定会修改modCount

  • 按下标删,也是先根据index找到Node,然后去链表上unlink掉这个Node。

  • 按元素删,会先去遍历链表寻找是否有该Node,考虑到允许null值,所以会遍历两遍,然后再去unlink它

  • 扩展:遍历链表的方式

for (Node x = first; x != null; x = x.next) {

}

Node x = first;

while(x!=null){

x = x.next;

}

set

public E set(int index, E element) {

checkElementIndex(index); //检查越界[0,size)

Node x = node(index);//取出对应的Node

E oldVal = x.item;//保存旧值 供返回

x.item = element;//用新值覆盖旧值

return oldVal;//返回旧值

}

改也是先根据index找到Node,然后替换值,改 不会修改modCount

get

根据index查询节点

  • get(int index)

public E get(int index) {

checkElementIndex(index);//判断是否越界 [0,size)

return node(index).item; //调用node()方法 取出 Node节点,

}

根据节点对象,查询下标

  • indexOf(Object o)

public int indexOf(Object o) {

int index = 0;

if (o == null) {//如果目标对象是null
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

我想问下大家当初选择做程序员的初衷是什么?有思考过这个问题吗?高薪?热爱?

既然入了这行就应该知道,这个行业是靠本事吃饭的,你想要拿高薪没有问题,请好好磨练自己的技术,不要抱怨。有的人通过培训可以让自己成长,有些人可以通过自律强大的自学能力成长,如果你两者都不占,还怎么拿高薪?

架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。

如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

中高级开发必知必会:

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

最后

我想问下大家当初选择做程序员的初衷是什么?有思考过这个问题吗?高薪?热爱?

既然入了这行就应该知道,这个行业是靠本事吃饭的,你想要拿高薪没有问题,请好好磨练自己的技术,不要抱怨。有的人通过培训可以让自己成长,有些人可以通过自律强大的自学能力成长,如果你两者都不占,还怎么拿高薪?

架构师是很多程序员的职业目标,一个好的架构师是不愁所谓的35岁高龄门槛的,到了那个时候,照样大把的企业挖他。为什么很多人想进阿里巴巴,无非不是福利待遇好以及优质的人脉资源,这对个人职业发展是有非常大帮助的。

如果你也想成为一名好的架构师,那或许这份Java核心架构笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

中高级开发必知必会:

[外链图片转存中…(img-m2fEgezR-1712072833261)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值