Debug LinkedList源码

前置知识

  • LinkedList基于链表,LinkedList的Node节点定义

  • 成员变量

     	//链表中元素的数量
    transient int size = 0; /**
    * 链表的头节点:用于遍历
    */
    transient Node<E> first; /**
    * 链表的尾节点:用于添加元素
    */
    transient Node<E> last;

2.1 Debug 分析第一个元素是如何进入链表的

编写测试代码,打上断点:

进入方法内部:

  • add方法默认添加到链表的尾部
  • 该方法等同于addLast(区别就是add方法需要返回一个true,而addLast没有任何返回值)

进入linkLast方法内部:

	/**
* 当前方法执行完后,若添加的节点为第一个节点,链表的last和first都指向新节点
*/
void linkLast(E e) {
//获取到链表的最后一个元素
final Node<E> l = last;
//创建一个节点,前一个节点为当前链表的last,后一个节点为空
final Node<E> newNode = new Node<>(l, e, null);
//修改last为新添加的节点
last = newNode;
//若链表为空,first节点为新添加的节点,否则添加前最后一个节点的next指向新节点
if (l == null)
first = newNode;
else
l.next = newNode; //链表长度加一
size++;
//链表修改次数加1
modCount++;
} //================================================================= Node带三个参数的构造函数:
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}

2.2 Debug 分析如何通过下标插入指定位置add(index,e)

编写测试用例:

进入方法内部:

public void add(int index, E element) {
//检查下标是否合法:大于等于0小于等于size【return index >= 0 && index <= size;】
checkPositionIndex(index); //若等于size,相当于在链表尾部添加节点
if (index == size)
linkLast(element);
else
//linkBefore中的Before指的是传入索引元素前
linkBefore(element, node(index));
}

进入node(index)方法:返回索引为index的元素

Node<E> node(int index) {
// assert isElementIndex(index); //查找元素的思路是遍历一半,先折半分区,然后若在小于折半的区域就从first开始往后遍历,反之从last往前遍历
//右移一位相当于除以2
if (index < (size >> 1)) {
//获取到first节点
Node<E> x = first;
//从first遍历到index的位置
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //获取到next的节点
Node<E> x = last;
//从last往前遍历到index的位置
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

回到linkBefore方法:

void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//获取index节点的上一个节点
final Node<E> pred = succ.prev;
//创建新节点,下一个节点指向index节点,上一个节点指向index节点的上一个节点
final Node<E> newNode = new Node<>(pred, e, succ);
//index节点的上一个节点指向变为指向新节点
succ.prev = newNode;
//**若index节点的头节点为空,相当于从头部插入节点,直接将新节点赋给first节点
if (pred == null)
first = newNode;
else
//index的上一个节点的下一个节点指向改为新节点
pred.next = newNode; //节点长度+1
size++;
//链表修改次数+1
modCount++;
}

2.3 Debug 分析如何通过下标获取指定元素

编写测试代码,打上断点:

进入get方法内部:

public E get(int index) {
//检查下标范围
checkElementIndex(index);
//遍历一半,返回节点的值
return node(index).item;
}

LinkedList支持的查询除了通过下标获取外,还支持getLast和getFirst

get(0)和getFirst时间复杂度有区别吗?

理论上来说,getFirst和getLast都是直接获取到节点,时间复杂度为O(1),而通过get(index)方法查询节点,都会折半后遍历一半的元素,时间复杂度为O(n)。但实际情况下,for循环遍历的第一个元素就100%是头节点或尾节点,不会进入之后的循环,实际运行的也是O(1)。

2.4 Debug 分析如何通过下标删除元素

打上断点:

进入方法内部:

public E remove(int index) {
//检查下标范围
checkElementIndex(index);
//node(index)获取需要删除的节点,折半遍历
return unlink(node(index));
}

进入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节点
first = next;
} else {
//若删除的节点为中间节点,将当前节点的上一个节点的下一个节点指向变为当前节点的下一个节点(step1)
prev.next = next;
//当前节点的上一个节点指向置为null(step2)
x.prev = null;
} if (next == null) {
//若删除的节点为尾节点,将当前节点的上一个节点赋给last
last = prev;
} else {
//若删除的节点为中间节点,将当前节点的下一个节点的上一个节点指向变为当前节点的上一个节点(step3)
next.prev = prev;
//当前节点的下一个节点指向置为null(step4)
x.next = null;
} //节点元素内容置为空
x.item = null;
//链表长度-1
size--;
//链表修改次数+1
modCount++;
//返回删除节点内容
return element;
}

2.5 Debug 分析如何通过对象删除节点(内容)

public boolean remove(Object o) {
//若节点为null,从first往下遍历(说明LinkedList是允许为空值的,并且允许多个)
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
}
//若节点不为空,遍历链表后删除节点
else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}

Debug LinkedList的更多相关文章

  1. jmeter sampler maven项目排错记

    eclipse 创建的maven项目,引入jar包之后出现红色叹号,一直找不到原因,连main方法都无法运行,提示找不到类: 错误: 找不到或无法加载主类 soapsampler.SoapSample ...

  2. Java集合框架源码分析(2)LinkedList

    链表(LinkedList) 数组(array)和数组列表(ArrayList)都有一个重大的缺陷: 从数组的中间位置删除一个元素要付出很大的代价,因为数组中在被删除元素之后的所有元素都要向数组的前端 ...

  3. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解

    [2013-12-06 11:06:21,715] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|7 ...

  4. Java总结 - List实现类ArrayList&LinkedList

    本文是根据源码进行学习的,如果我有什么理解不对的地方请多指正,谢谢您 上面基本就是List集合类的类图关系了,图中省略掉了比如Cloneable等标记接口,那么List分别具体的主要实现类有:Arra ...

  5. 【Java源码】集合类-LinkedList

    一.类继承关系 LinkedList和ArrayList都实现了List接口.所以有List的特性,同时LinkedList也实现了Deque,所以它也具有双端队列和栈的特性. public clas ...

  6. DEBUG ArrayList

    1,ArrayList面试必问 说说ArrayList和LinkedList的区别? ArrayList基于数组实现,LinkedList基于链表实现,不同的数据结构决定了ArrayList查询效率比 ...

  7. Debug HashMap

    目录 1,HashMap面试必问 2,Debug源码的心得体会 3,JDK 1.7 3.1 用debug分析一个元素是如何加入到HashMap中的[jdk1.7] 3.2 用debug分析HashMa ...

  8. Java自学第6期——Collection、Map、迭代器、泛型、可变参数、集合工具类、集合数据结构、Debug

    集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型值. ...

  9. 记一次debug记录:Uncaught SyntaxError: Unexpected token ILLEGAL

    在使用FIS3搭建项目的时候,遇到了一些问题,这里记录下. 这里是发布搭建代码: // 代码发布时 fis.media('qa') .match('*.{js,css,png}', { useHash ...

随机推荐

  1. 【Python3爬虫】模拟实现小牛在线登录过程

    一.站点分析 小牛在线的登录入口地址为:https://www.xiaoniu88.com/user/login. 用户登录时除了需要输入用户名和密码,还要输一个验证码.我们可以先任意输入一个用户名. ...

  2. Centos 6.4 安装KSnapshot 和gimp截图工具

    一. # wget http://www.ibiblio.org/pub/Linux/X11/xutils/ksnapshot-0.2.7.tar.gz # tar -zxvf ksnapshot-0 ...

  3. Quartz.Net系列(九):Trigger之CronScheduleBuilder和Cron表达式详解

    1.使用 var scheduler =await StdSchedulerFactory.GetDefaultScheduler(); await scheduler.Start(); var jo ...

  4. java语言基础(五)_Scanner类_Random类_ArrayList类

    API API(Application Programming Interface),应用程序编程接口.Java API是一本程序员的字典 ,是JDK中提供给程序员使用的类的说明文档.这些类将底层的代 ...

  5. 【PyMuPDF和pdf2image】Python将PDF转成图片

    前言: 在最近的测试中遇到一个与PDF相关的测试需求,其中有一个过程是将PDF转换成图片,然后对图片进行测试. 粗略的试了好几种方式,其中语言尝试了Python和Java,总体而言所找到的Python ...

  6. https http 混合访问_https 页面中引入 http 资源的解决方式

    解决android 5.0 webview不能加载http与https混合内容的问题 在使用WebView加载https资源文件时,如果认证证书不被Android认可,那么会出现无法成功加载对应资源问 ...

  7. 轻松让HTML5可以显示桌面通知Notification非常实用

    使用Notification的流程 1.检查浏览器是否支持Notification2.检查浏览器的通知权限3.如果权限不够则申请获取权限4.创建消息通知5.展示消息通知 Notification AP ...

  8. 在页面制作的时候常用的html页面滚动加载,可视区域判断方法

    演示图 考虑2个情况一种情况初始状态下 滚动到在中间区域的时候,这时上半部分看不见的元素就不给字体添加红色一种情况是,从头向下看的. 代码 .ss li { margin: 40px; } <d ...

  9. css3中样式计算属性calc()的使用和总结

    calc的介绍 在css3样式中有一个类似与函数的计算属性calc(),它主要用于指定元素的长度,无论是border.margin.pading.font-size和width等属性都可以使用calc ...

  10. HTML5(四)Drag and Drop

    HTML5 拖放(Drag 和 Drop) 拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置. 在 HTML5 中,拖放是标准的一部分,任何元素都能够拖放. 设置元素为可拖放 首先,为了使元素 ...