Java LinkedList【笔记】
Java LinkedList【笔记】
LinkedList
LinkedList 适用于要求有顺序,并且会按照顺序进行迭代的场景,依赖于底层的链表结构
LinkedList基本结构
LinkedList 底层数据结构是一个双向链表
链表每个节点叫做 Node,Node 有 prev 属性,代表前一个节点的位置,next 属性,代表后一个节点的位置
双向链表的头节点(first)的前一个节点是 null
双向链表的尾节点(last)的后一个节点是 null
当链表中没有数据时,first 和 last 是同一个节点,前后指向都是 null
因为是个双向链表,只要机器内存足够强大,是没有大小限制的
链表中的元素叫做 Node,初始化参数的顺序为前一个节点,本身节点值,后一个节点
LinkedList追加节点(新增节点)
在LinkedList追加节点的时候,可以选择追加到链表头部,也可以选择追加到链表尾部,add是默认为尾部追加,addfirst是从头部追加
从头部开始追加(add)
简单来说,尾部追加节点只需要把指向位置修改下就行了
具体操作,我们首先需要将尾节点的数据暂时存储起来,然后建立一个新的节点,初始化,需要注意,新节点的前一个节点的值为尾节点值,新增节点的后一个节点为nul,然后将新建的节点追加到尾部,如果链表为空,头部和尾部都是同一个节点,都是新建的节点,否则把前尾节点的下一个节点指向当前尾节点
源码:
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
从头部追加的方法(addfirst)
将头节点赋值给临时变量,然后新建节点,前一个节点指向null,新建节点的下一个节点的值为头节点的值,然后将新建节点变成头节点,如果头节点为空,那么就是链表为空,头尾节点就是一个节点,此时上一个头节点的前一个节点就指向当前节点
源码:
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
通过这两个方法的对比,可以发现头部追加节点和尾部追加节点是非常相像的,前者是移动头节点的 prev 指向,后者是移动尾节点的next指向
LinkedList节点删除
和追加有一些类似,也是可以选择从头部删除或者是从尾部删除,删除的时候会把节点的值以及前后指向节点都变为null,这样有利于垃圾回收(GC)
从头部删除的话,首先我们先拿出头节点的值来作为方法的返回值,拿出头节点的下一个节点来,将前后设为null帮助垃圾回收,然后头节点的下一个节点成为头节点,如果next为空,则说明链表为空,如果链表不为空,则将头节点的下一个节点指向null,然后修改一下链表大小
源码:
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null)
last = null;
null
else
next.prev = null;
size--;
modCount++;
return element;
}
可以发现,链表结构的节点新增和删除都只需要将前后节点的指向修改下就可以,这说明LinkedList的新增和删除速度很快
LinkedList节点查询
链表的查询是比较慢的,而在LinkedList中进行节点查询不是按照从头循环到尾的方法,而是用的简单二分法,先看index是在链表的前半部分还是后半部分,如果是前半边,就从头找,如果是后半边,就从尾找,这样可以提高一些性能,而且因为链表的性质,即只能从第一个或者最后一个去访问其他的元素,所以简单二分法已经是最优解了
源码:
Node<E> node(int index) {
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
LinkedList中的接口的新增方法
LinkedList迭代器
LinkedList使用ListIterator迭代接口来实现双向的迭代访问,这个接口提供了向前以及向后的迭代方法
从头到尾的方向的迭代
首先判断一下有没有下一个元素,如果下一个节点的索引小于链表的大小,那么就说明有下一个元素,然后我们取一个元素,看一下版本号有无变化,然后再检查一遍,next是当前的节点,在上一次执行next()方法的时候被赋值的,如果是第一次执行,那么就是在初始化迭代器的时候被赋值的,然后next是下一个节点,为下一次的迭代做准备
源码:
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
从尾到头的迭代
首先我们要确定上次节点的索引位置,如果上次节点索引位置大于0,那么就代表有节点可以迭代,然后我们取前一个节点,同样检查版本号,在next为空的时候,有两个可能,第一个,说明这是第一次迭代,取尾节点,第二个,上次操作的时候把尾节点删掉了,而在next不为空的时候,这就说明已经发生过迭代,直接去前一个节点就可以了,然后改变索引位置
源码:
public boolean hasPrevious() {
return nextIndex > 0;
}
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
(next.prev)
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
一些问题:
ArrayList 和 LinkedList 有何异同?
不同:
底层数据结构方面
ArrayList 底层是数组
LinkedList 底层是双向链表
应用场景方面
ArrayList 更适合于快速的查找匹配,不适合频繁新增删除
LinkedList 更适合于经常新增和删除,对查询反而很少的场景
相同:
最大容量
ArrayList 有最大容量的,为 Integer 的最大值,大于这个值 JVM 是不会为数组分配内存空间的
LinkedList 底层是双向链表,理论上可以无限大,但是实际上,LinkedList 实际大小用的是 int 类型,这也说明了 LinkedList 不能超过 Integer 的最大值,不然会溢出
** null 值**
ArrayList 允许 null 值新增,也允许 null 值删除
LinkedList 新增删除时对 null 值没有特殊校验,是允许新增和删除的。
线程安全
当两者作为非共享变量时,比如说仅仅是在方法里面的局部变量时,是没有线程安全问题的,只有当两者是共享变量时,才会有线程安全问题
Java LinkedList【笔记】的更多相关文章
- Java开发笔记(六十七)清单:ArrayList和LinkedList
前面介绍了集合与映射两类容器,它们的共同特点是每个元素都是唯一的,并且采用二叉树方式的类型还自带有序性.然而这两个特点也存在弊端:其一,为啥内部元素必须是唯一的呢?像手机店卖出了两部Mate20,虽然 ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- 20145330第五周《Java学习笔记》
20145330第五周<Java学习笔记> 这一周又是紧张的一周. 语法与继承架构 Java中所有错误都会打包为对象可以尝试try.catch代表错误的对象后做一些处理. 使用try.ca ...
- Java学习笔记4
Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...
- Java开发笔记(序)章节目录
现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...
- Java开发笔记(七十)Java8新增的几种泛型接口
由于泛型存在某种不确定的类型,因此很少直接运用于拿来即用的泛型类,它更经常以泛型接口的面目出现.例如几种基本的容器类型Set.Map.List都被定义为接口interface,像HashSet.Tre ...
- JAVA自学笔记15
JAVA自学笔记15 @例题1:共有5个学生,请把五个学生的信息存储到数组中,并遍历数组,并获取每个学生的信息 Students[] students=new Student[5]; Student ...
- JAVA自学笔记19
JAVA自学笔记19 1.集合总结 Collection(单列集合) List(有序可重复) ArrayList:底层数据结构是数组 ,查询快,增删慢.线程不安全,效率高 Vector:底层数据结构是 ...
- JAVA自学笔记17
JAVA自学笔记17 1.Map接口 1)概述 将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值.可以存储键值对的元素 2)与Collection接口的不同: ①Map是双列的 ...
随机推荐
- Python装饰器-给你的咖啡加点料
今天你的咖啡加糖了吗? 让我们通过一个简单的例子来引出装饰器的概念及用法.在引出装饰器之前,我们先来了解一下函数的概念. 一.函数回顾 1.在python中函数是一等公民,函数也是对象.我们可以把函数 ...
- DRF之权限和频率限制
一.权限 权限可以限制用户对视图的访问和对具体数据对象的访问. 在执行视图的dispatch方法前,会先进行视图访问权限的判断 在通过get_object获取对象时,会进行模型对象访问权限的判断 源码 ...
- 码云使用svn无法提交空文件夹
错误信息: svn: E200015: Commit failed (details follow): svn: E200015: Empty directories is not supported ...
- CF1539C Stable Groups[题解]
Stable Groups 题目大意 给定 \(n\) 个数 \(a_i\) ,你可以将这些数分成若干组,但每一组的元素满足按升序排列后,相邻两个元素值的差不超过 \(x\) .在分组前,你可以向这些 ...
- C语言不明白
C语言查看多字节变量中每单个字节数据的方法代码: #include<stdio.h> void main() { int a=0x21109225;char* pAddress=(char ...
- 何为“Secure Contexts”安全内容? 终于说明白了!
何为"Secure Contexts"安全内容? 终于说明白了! 看图说话 [途径1]:地址栏输入: edge://flags/ 按需设置选项后,重启浏览器即可. Allow ...
- 一:Vue项目构建
第一步:需要安装nodeJS的环境,直接去官网下载https://nodejs.org/en/,下载下来按照提示一步步的安装.(vue.js是一个Js 框架.在node里面通过Npm 安装,是为了方便 ...
- .net core番外第2篇:Autofac的3种依赖注入方式(构造函数注入、属性注入和方法注入),以及在过滤器里面实现依赖注入
本篇文章接前一篇,建议可以先看前篇文章,再看本文,会有更好的效果. 前一篇跳转链接:https://www.cnblogs.com/weskynet/p/15046999.html 正文: Autof ...
- Redis双写一致性与缓存更新策略
一.双写一致性 双写一致性,也就是说 Redis 和 mysql 数据同步 双写一致性数据同步的方案有: 1.先更新数据库,再更新缓存 这个方案一般不用: 因为当有两个请求AB先后更新数据库后,A应该 ...
- Linux中tomcat随服务器自启动的设置方法
1. cd到rc.local文件所在目录,一般在 /etc/rc.d/目录. 2. 将rc.local下载到本地windows系统中. 3. 编辑rc.local,将要启动的tomcat /bin/ ...