# JDK成长记6:你了解LinkedList的五脏六腑么?

# JDK成长记6:你了解LinkedList的五脏六腑么?_第1张图片

# JDK成长记6:你了解LinkedList的五脏六腑么?_第2张图片

上一节你看过了LinkedList的add方法源码,是不是已经打开了思路呢?其实核心原理就是辅助指针+Node双向链表数据结构而已。

相信经过前面的学习,你应该热身完毕了,之后的学习可以让我们可以加快速度了。

GO!GO!

这一节你还需要深入LinkedList的其他方法探索下它们的底层原理是什么。

学完这一节,你可以自己写出一个LinkedList,甚至可以举一反三写出单向链表的List,逆转一个单向链表等等。你也可以攻克很多链表的算法题。面试问到ArrayList和LinkedList的时候,相信你也会信手拈来。

让我们一起开始吧!

另一个add(int index,E e)方法

另一个add(int index,E e)方法

有了之前的经验,相信现在你不需要我在一步一步带着你看源码原理了。我直接将核心源码+配图的方式给大家讲解下原理。你更多的是要根据我的这个思路自己把源码阅读一下,自己画图讲给别人听听才是最好的学习方法。

首先来看下另一个add方法,在某个位置增加一个元素。核心源码原理图如下所示:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第3张图片

从add方法源码脉络可以看到,当调用的add方法时,会根据要插入的位置index判断,是否向尾节点添加元素。如果是和上一节讲的普通add(E e)方法一样,调用了linkLast方法而已。主要不同的是当不是向尾节点添加元素时,调用了linkBefore方法。

而 linkBefore核心是通过node(int index)定位元素的。如下图所示:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第4张图片

先看源码的脉络,核心是一个if,两个for循环。再看下细节,if条件是什么意思?如图中所示,就是进行了右移1,和除以2相似,类似于二分法的思想。接着for循环,通过x.next或x.prev开始从前或者从后向前寻找元素。

这个思路是不是,还是个不错的亮点,很多时候算法都会有二分法,或者分而治之的思想。

当通过node方法确认了index位置的元素后,就开始执行linkBefore方法了。如下图所示:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第5张图片

上面源码脉络,主要分三步,你可以跟着如下思路看:

1、将之前node定位的元素记为succ传入,通过一个辅助指针pred指向定位元素的前一个节点。

2、之后创建一个newNode,它的prev指针指向pred所在位置,尾指针指向succ,也就是定位的元素。

3、最后修改定位元素的prev指针,指向新元素,修改pred位置的元素尾指针指向新元素。

最后就会形成上图的结果了。其实你可以发现,和linkLast很像,linkLast是通过辅助指针l指向last,来帮助在结尾添加节点的。而linkBefore是通过,先定位元素后,用定位元素的prev前一个节点作为辅助,插入元素的。你只要记住这个思路,一定能自己手写出来两种add方法的。

remove系列方法

remove系列方法

LinkedList的remove方法,大多都很相似,都是一些指针变换。你分析的最好方法还是画图。

相信到这里,你已经掌握基本分析源码的方式和方法了,这里就不再一步一步带大家看源码了。直接给大家讲解下源码的原理图可以了。

首先是removeLast方法的源码原理图,如下所示:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第6张图片

源码的核心的脉络就是使用一个l指针,记录了尾节点的位置,之后通过l.prev找到之前的元素,记录下为prev,和最后一个元素断开l.next=null,让last指针指向新的尾节点prev即可。

再看看removeFirst方法的原理图:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第7张图片

你其实可以看到和之前removeLast很像,只不过是使用f指针,记录了头节点位置,之后next记录下f.next。断开和原头节点的连接next.prev=null。最后再让first指针指向新的头结点。

其实你只要记住这个思路就可以。双向链表使用一个辅助指针记录prev或next的位置+头/尾指针,就可以非常简单的实现从头节点或者尾节点删除元素。手写应该也不是什么难事。

最后你来看下remove(index)方法的源码原理图:

# JDK成长记6:你了解LinkedList的五脏六腑么?_第8张图片

可以看到,remove(index)方法,通过node方法定位元素,之后使用了两个辅助指针,prev和next分别记录了所定位元素的前后节点。

定位元素的源码原理上一节已经讲过了,就是二分法+for循环遍历查找而已。通过prev和next两个指针,就可以分别断开定位元素x的前后指针,并且next和prev重新相接,就实现了从某个位置删除元素了。

核心原理,就是定位+前后辅助指针来实现的,你要记住这点思路就可以自己尝试手写remove方法了。

到这里LinkedList的核心方法就带大家看完了,它还有一些高级方法,addAll、toArray等等,你可以自己简单的看下,现在对你其实没什么难度的。大家看这些方法找到核心原理总结下来就好。

另外,Vector和Stack这两个集合类完全和ArrayList很像。除了Vector扩容默认是两倍,是线程安全的,底层通过synchronized来实现的。Statck继承了Vector,通过数组实现了类似栈的数据结构。大家之后可以简单扫一下就可以了,除了很老的源码代码或者业务程序,现在基本不会使用这两个集合类了,因为底层的synchronized锁太影响性能了。

这一节你应该学到的一个思想就是,总结。当你按照脉络看完源码后,画完图后,不能就此结束了。一定要总结一个要点和几个要点出来。总结体现了做一件事的输出结果。做任何事情都是样的,比如运维管理IDC机柜机器,要出一个excel记录每台机器的采购时间,电池情况,硬盘情况等等。excel就是一个输出结果,一个总结。

你在工作中,很多事情要以一个结果为结束才是最好的。文档也好,流程点、里程碑也好,最好你要总结记录下这个结果。相信你坚持这样做会很有很多好处的。

金句甜点

金句甜点

除了今天知识,技能的成长,给大家带来一个金句甜点,结束我今天的分享:坚持的三个秘诀之一视觉化。

相信你肯定有会觉得过,坚持很难,可能你坚持1周还好,但是坚持1个月,1年,2年,5年,10年,一辈子…… 总有一个让你感觉,好像真的坚持好难。但是其实很多时候不是坚持很难,而是你觉得值不值得的问题。这是根本原因,你可以仔细想想,是不是因为这个观念所导致的。

除了要意识到这个观念以外,到底怎么才能坚持一件事情呢?之后的文章我会给大家分享坚持的3个秘诀。首先第一个秘诀就是视觉化。给大家举个亲身经历吧,我毕业后工作了几年,从一个瘦瘦的程序员,变成了带游泳圈的瘦瘦程序员。于是有一天我决定要减脂塑形,想要恢复原来的肚子。不知道你们是不是也遇见过这种场景。于是我首先把我喜欢的一个偶像的有型的照片,有腹肌那种海报,贴在了家里一个比较随意的地方,还附上了我自己的一个照片。那个画面简直了。。。我开始控制饮食,逐渐增加锻炼强度和频次,每次我不想运动的时候就会不经意的看到海报和我自己的照片,我再看看我的小肚子,就又开始锻炼了。就着这样不知不觉就坚持了1个月,果然有了效果,竟然体脂率下降了2%,体重下降了11斤,肚子小了一大圈,就是肌肉量没啥变化,反正没少就不错了。接着,我就形成习惯了,再次在增加运动强度和频次,因为我一直想着有一天程序员能有6块或者8块腹肌,那个场景好让人向往啊。所以我一直在坚持着。

其实视觉化的本质就是让你感觉坚持的事情很值。你喜欢演讲,你可以想象,有一天你站在上千人的听众面前,你声情并茂的做了一场精彩的演讲,那个场景就可以激励你每天不断的练习和学习,你想要晋升,你就不会睡懒觉,每天不会睡到自然醒,因为每当你起不了床的时候,你心中想象到一个场景,你晋升后可以给父母多寄些钱,给妻子、孩子买好的东西,可以减轻房贷或者车贷,可以帮助感恩之前帮助过的你的人等等,总有一个会让你觉的值的理由,让你坚持下去的,是不是?当然你喜欢学习,你也可以每周坚持看几篇成长记来提升自己,相信成长会让你变的更有价值,更有自信。

记住你人生中每走过的一步路,都不会被辜负的。总有一天又体现出来的。加油吧,各位!坚持吧,各位!

最后,你可以阅读完源码后,在茶余饭后的时候问问同事或同学, 你也可以分享下,讲给他听听。

欢迎大家在评论区留言和我交流。

本文由博客一文多发平台 OpenWrite 发布!

你可能感兴趣的:(java)