java linkedlist 更新_Java填坑系列之LinkedList
前言
今天我们通过分析LinkedList的源码,来学习一下它内部是如何添加、查询以及删除元素的。同时在阅读源码时,也要思考以下几个问题。
LinkedList的底层数据结构是什么?
与ArrayList有什么区别?
LinkedList是线程安全的吗?
继承关系
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
{
//......
}
复制代码
首先我们来看一下LinkedList的继承关系,可以看出它继承自AbstractSequentialList。并且实现List、Deque、Cloneable以及Serializable接口,然后我们再来看一下它的核心字段。
核心字段
//表示LinkedList内部的元素数量
transient int size = 0;
//表示头结点
transient Node first;
//表示尾节点
transient Node last;
复制代码
从源码中可以看出LinkedList中存在两个特殊的节点,分别是头结点和尾节点。那我们是不是就能猜到LinkedList底层结构是一个双向循环链表?到底是不是,我们慢慢分析。
构造方法
//创建了一个空列表
public LinkedList() {
}
//这个构造方法传入一个集合,然后将该集合的元素全部添加的链表中
public LinkedList(Collection extends E> c) {
this();
addAll(c);
}
复制代码
可以看出构造方法比较简单,我们再来看一下Node类。
Node
private static class Node {
E item; //元素
Node next; //后节点
Node prev; //前节点
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
复制代码
Node节点类是LinkedList中一个私有的静态内部类,包含元素、前节点和后节点。
添加
添加一个节点
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
//l为尾节点
final Node l = last;
//创建一个以l节点为前节点,数据为e,后节点为null的Node节点,此时newNode为尾节点
final Node newNode = new Node<>(l, e, null);
//更新尾节点
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
复制代码
从源码中可以看出LinkedList添加一个元素是从链表尾部进行添加,添加步骤如下:
将尾节点赋值l节点
创建一个以l节点为前节点,数据为e,后节点为null的Node节点
更新尾节点
判断l节点是否为空
size++、modCount++
通过指定index添加
public void add(int index, E element) {
//判断index是否越界
checkPositionIndex(index);
if (index == size)
//此时表示在链表尾部添加
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node succ) {
// succ表示index对应的节点,pred表示succ前节点
final Node pred = succ.prev;
//newNode的前节点为pred,后节点为succ
final Node newNode = new Node<>(pred, e, succ);
//succ的前节点为newNode
succ.prev = newNode;
//如果pred为null,说明succ原来是头结点,而现在succ的前节点为newNode,所以现在头结点是newNode
if (pred == null)
first = newNode;
else
//pred的后节点为newNode
pred.next = newNode;
size++;
modCount++;
}
复制代码
修改
首先我们来看通过指定索引来修改Node数据,源码如下
public E set(int index, E element) {
//检查是否数组越界
checkElementIndex(index);
//通过node方法来获得对应得Node节点
Node x = node(index);
//保存旧数据
E oldVal = x.item;
//赋值新数据
x.item = element;
//返回旧数据
return oldVal;
}
复制代码
可以看出修改数据主要分为以下几步:
获得index对应的Node节点
保存Node节点旧数据
赋值新数据
获取
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
复制代码
可以看出get()方法内部只有两行代码,我们分别来看一下都是做了什么操作。
checkElementIndex(index)
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
复制代码
可以看出checkElementIndex()方法主要是来判断index是否数组越界,如果越界就抛出对应的异常。
node(index)
Node node(int 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;
}
}
复制代码
可以看出node()方法通过判断index是处于前半段还是后半段,来查找对应的Node节点。通过折半查找提升了一定的效率。
删除
删除指定索引对应的节点
public E remove(int index) {
checkElementIndex(index);
//传入index对应的Node节点
return unlink(node(index));
}
E unlink(Node x) {
//获取Node节点数据
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;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
//将节点数据置为null
x.item = null;
size--;
modCount++;
return element;
}
复制代码
然后我们再来简单看下LinkedList中其他remove()方法,具体如下:
//删除第一个节点数据
public E removeFirst() {
final Node f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//删除最后一个节点数据
public E removeLast() {
final Node l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
复制代码
总结
通过分析LinkedList的源码,我们可以知道LinkedList在插入和删除上有着比较大的优势,这也符合链表的特性。而且通过判断index在前半段还是后半段,使用折半查找的方法来获得对应的Node节点,提升了一定的效率。
参考资料
java linkedlist 更新_Java填坑系列之LinkedList相关推荐
- MySQL填坑系列--Linux平台下MySQL区分大小写问题
问题引入 大家好,我是软件大盗(道),下面开始我们的<MySQL填坑系列>. 笔者最近又在MySQL的边缘试探,然后,试探着,试探着就报错了. 情景还原 书接上文,系统连接数据库时报错:找 ...
- Java填坑系列之SparseArray
前言 今天我们来了解一下与HashMap类似的数据结构SparseArray,并分析下它的源码实现.在分析源码的过程中,我们带着以下几个问题来看. SparseArray底层数据结构是什么? Spar ...
- java游戏代码_Java与Kotlin系列文章之性能问题详解
作者丨Jakub Anioła 译者丨姜雨生 策划丨田晓旭 随着对 Kotlin 越来越深入的了解,我发现市面上关于 Kotlin 方面,比较深入的资料几乎是 0,所以我决定,将 Kotlin 各个方 ...
- java 动态更新_java动态更新枚举类
工作中遇到需要对枚举类的值进行动态更新 手动改不现实也不方便 现记录下来方便以后学习使用 1.在工程utils包中添加动态更新枚举类得工具类(根据自己得项目,放到指定位置调用就可以) 2.一开始陷入了 ...
- android小米定位,Android填坑系列:在小米系列等机型上放开定位权限后的定位请求弹框示例...
背景 近期因实际项目需要,在特定操作下触发定位请求,取到用户位置及附近位置. 问题: 经初步选型,最终决定接入百度定位,按照百度定位SDK Android文档,接入过程相对顺利. 但随后发现,在小米系 ...
- java数组更新_java数组
数组无论在哪种编程语言中都算是最重要的数据结构之一,同时不同语言的实现及处理也不尽相同.但凡写过一些程序的人都知道数组的价值及理解数组的重要性,与链表一道,数组成为了基本的数据结构.尽管Java提供了 ...
- java jlist 更新_java – 更新JList
我现在已经创建了一个基于arraylist的JList,并且由defaultlistmodel填充.该列表将在人们连接到服务器时添加人员,但不会显示连接的人或连接后的人.所以,我必须更新JList. ...
- java正则表达式爬虫_Java简单爬虫系列(3)---正则表达式和Java正则API的使用
上一篇内容写了如何请求资源,那么资源请求下载之后我们就要对它就行解析了,解析之前我们先熟悉一下正则表达式 正则表达式在平常使用时还是很广泛的,比如说表单输入验证,验证手机号邮箱之类,Java的字符串匹 ...
- java数组更新_java刷新数组到jList
好吧,所以我有一个JList和内容提供了一个数组.我知道如何将元素添加到数组,但我想知道如何刷新JList ...或者甚至有可能吗?我试过谷歌. :\java刷新数组到jList import jav ...
最新文章
- 文件查找命令find的使用
- 单片机值得学吗?会单片机能找什么工作?
- 最长回文子串-三种DP实现
- html音频从10秒播放至30秒,基于Arduino制作SD卡音乐播放器
- java 编程工具_Java开发工具可以促进编程!
- 求你了,别再说Java对象都是在堆内存上分配空间的了!
- iPhone 5用户们,苹果又喊你更新了,不然可能会变砖!
- python基本词汇的特点_开课吧老师为你讲解 Python都有什么优点?
- Spark DataFrame入门详解
- 18. jQuery - 尺寸
- rabbitmq 一个生产者多个消费者_RabbitMQ入门学习系列(二),单生产者消费者
- jquery全国省市县三级联动
- junit4报测试类class not found
- linux内核源码分析之虚拟内存映射
- 跟锦数学200217 厦门大学2019年数学分析考研试题4 (解答见跟锦数学微信公众账号)...
- PEP 635 – Structural Pattern Matching: Motivation and Rationale
- 微软Kinect是怎么做到的
- 抢菜捡漏工具(PrintScreenCatchImg)
- CTF easycap Banmabanma
- 计算机锁屏打不开,电脑点锁屏锁不了怎么办
热门文章
- 决策树可视化案例python_Python决策树demo可视化
- 内存泄漏的原因及解决办法_内存泄漏的场景和解决办法
- linux ping策略打开_Linux Iptables允许或阻止ICMP ping请求
- vue中接受后台传过来的图片文件流blob前端进行展示实现方法
- 标准气压高度与修正海平面气压的区别
- @Autowired 与@Resource的区别
- springboot线程池使用
- Git实现从本地添加项目到远程仓库
- Swagger注解-@ApiImplicitParams 和 @ApiImplicitParam
- 红帽yum安装httpd出现错误(This system is not registered to Red Hat Subscription Management. You can use subs)