集合家族——LinkedList
一、概述:
LinkedList 与 ArrayList 一样实现 List 接口,只是 ArrayList 是 List 接口的大小可变数组的实现,LinkedList 是 List 接口链表的实现。基于链表实现的方式使得 LinkedList 在插入和删除时更优于 ArrayList,而随机访问则比 ArrayList 逊色些。
二、源码分析:
2.1 定义方法:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
从这段代码中我们可以清晰地看出 LinkedList 继承 AbstractSequentialList,实现 List、Deque、Cloneable、Serializable。其中 AbstractSequentialList 提供了 List 接口的骨干实现,从而最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,从而以减少实现 List 接口的复杂度。
2.2 属性
transient int size = 0;//元素个数
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;//头结点
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;//尾节点
2.3 内部类Node
private static class Node<E> {
E item;//当前元素节点
Node<E> next;//下一个节点
Node<E> prev;//上一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Node为 LinkedList 的内部类,它定义了存储的元素。该元素的前一个元素、后一个元素,这是典型的双向链表定义方式
2.4 构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);//调用addAll()方法,下面详解
}
2.5 添加方法
linkedList中存在着许多添加方法,这里找几个典型方法进行剖析
2.5.1 add():将指定元素添加到此列表的结尾
public boolean add(E e) {
linkLast(e);
return true;
}
上面显示add方法调用了linkLast(E e) 方法,我们点进去看
void linkLast(E e) {
//得到最后一个节点
final Node<E> l = last;
//创建一个新的节点,将原尾节点设置此节点的前置节点,上面Node内部类有参入传入解析
final Node<E> newNode = new Node<>(l, e, null);
//让新节点成为最后的节点
last = newNode;
//如果原尾节点是空的,则代表原来没有元素节点,则头节点也是新插入节点
if (l == null)
first = newNode;
//如果不是空的,原尾节点的后置节点便是新插入节点,因为是双向链表
else
l.next = newNode;
//元素节点个数+1
size++;
//统计修改次数
modCount++;
}
2.5.2 add(int index, E element) :在指定未知插入节点
public void add(int index, E element) {
//检验下标越界
checkPositionIndex(index);
//如果下标等于大小,则执行linkList,上面已经分析过
if (index == size)
linkLast(element);
//如果不相等,则执行linkBefore
else
linkBefore(element, node(index));
}
在进入linkBefore之前,我们首先进入node()方法中进行解析
node(int index):用来查找 index 位置的节点元素。
Node<E> node(int index) {
// assert isElementIndex(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;
}
}
下面我们进入linkBefore(E e, Node<E> succ) 中去看
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//e 要插入的节点 succ 插入位置原有的节点
//获取要插入位置的前置节点,然后创建新节点,传参
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
//将succ的前置节点设置为新节点
succ.prev = newNode;
//如果succ前置节点为空,则此时新节点为头结点
if (pred == null)
first = newNode;
//否则将前置节点的后置节点设置为新节点
else
pred.next = newNode;
size++;
modCount++;
}
2.5.3 addAll(Collection<? extends E> c) :将 Collection 中的所有元素添加到列表中
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
这里面经过上面两个方法的介绍,其实也不难理解,即将collection中的元素转换成Object数组,然后再赋值,赋值也是调用上面解析的方法。当然还有各色各样的添加方法,可以直接添加头,添加尾,其道理都一样
2.6 删除
2.6.1 remove(int index):删除指定下标的元素节点
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
这里主要是unlink(Node<E> x)方法,进去剖析
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 = next;
} else {
prev.next = next;
x.prev = null;
}
//同理
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
2.6.2 remove(Object o) :删除指定内容的元素节点
public boolean remove(Object o) {
//判断内容是否为空
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
//调用unlink()方法
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;
}
删除方法也有很多方法,比如删除头,删除尾等,不过多解释
2.7 查询方法 get(int index)
public E get(int index) {
checkElementIndex(index); //node(index)方法,上面已经解析过
return node(index).item;
}
集合家族——LinkedList的更多相关文章
- Collection集合家族
集合家族 数组:存储相同类型的多个元素 对象:存储不同类型的多个元素 集合:存储多个不同类型的对象 List List继承自Collection接口,是有序可重复的集合. 它的实现类有:ArrayLi ...
- 【Java集合】LinkedList详解前篇
[Java集合]LinkedList详解前篇 一.背景 最近在看一本<Redis深度历险>的书籍,书中第二节讲了Redis的5种数据结构,其中看到redis的list结构时,作者提到red ...
- JDK集合框架--LinkedList
上一篇讲了ArrayList,它有一个"孪生兄弟"--LinkedList,这两个集合类总是经常会被拿来比较,今天就分析一下LinkedList,然后总结一下这俩集合类的不同 首先 ...
- Java自学-集合框架 LinkedList
Java集合框架 LinkedList 序列分先进先出FIFO,先进后出FILO FIFO在Java中又叫Queue 队列 FILO在Java中又叫Stack 栈 示例 1 : LinkedList ...
- 集合框架——LinkedList集合源码分析
目录 示例代码 底层代码 第1步(初始化集合) 第2步(往集合中添加一个元素) 第3步(往集合中添加第二个元素) 第4步(往集合中添加第三个元素) LinkedList添加元素流程示意图 第5步(删除 ...
- IT第二十一天 - Collections、ArrayList集合、LinkedList集合、Set集合、HashMap集合、集合的操作注意【修20130828】
NIIT第二十一天 上午 集合 1. 集合Collection存储数据的形式是单个存储的,而Map存储是按照键值对来存储的,键值对:即键+值同时存储的,类似align="center&quo ...
- Java 集合之LinkedList源码分析
1.介绍 链表是数据结构中一种很重要的数据结构,一个链表含有一个或者多个节点,每个节点处理保存自己的信息之外还需要保存上一个节点以及下一个节点的指针信息.通过链表的表头就可以访问整个链表的信息.Jav ...
- java集合系列——List集合之LinkedList介绍(三)
1. LinkedList的简介 JDK 1.7 LinkedList是基于链表实现的,从源码可以看出是一个双向链表.除了当做链表使用外,它也可以被当作堆栈.队列或双端队列进行操作.不是线程安全的,继 ...
- java学习笔记之集合家族1
集合 集合介绍: 由于数组中存放对象,对对象操作起来不方便.java中有一类容器,专门用来存储对象. 集合与数组的区别: 1.数组的长度固定的,而集合长度时可变的 2.数组只能储存同一类型的元素,而且 ...
随机推荐
- 输入一个正整数n,输出所有和为n的连续正整数序列
public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (true) { System. ...
- S02_CH02_MIO实验Enter a post title
S02_CH02_MIO实验 2.1 GPIO简介 Zynq7000系列芯片有54个MIO(multiuse I/O),它们分配在 GPIO 的Bank0 和Bank1隶属于PS部分,这些IO与PS直 ...
- gdb暂停或恢复程序的运行
ref : https://blog.csdn.net/seu_lyr/article/details/9050657 一 暂停程序的运行: (一)GDB的暂停方式:断点(BreakPoint). ...
- 怎样在Chrome浏览器上安装 Vue Devtools 扩展程序
第一步: 前往 GitHub 下载 Vue Devtools 项目文件 https://github.com/vuejs/vue-devtools 注意: 1. 将分支切换为 master 2. 下载 ...
- 利用Mathpix Snipping Tool轻松在markdown/LaTeX中输入电子书和论文的数学公式
最近写图形学博客写累了,公式太多了,一个个输入实在太累,所以从数学建模队友那里吃了一个安利. 官网下载 下载安装后,直接新建一个截图,就可以转成LaTeX数学公式了.效果如下: 爽的一批啊!!! 另外 ...
- Java并发(思维导图)【待评估、删除】
1, 2, 3,常用函数 Semaphore import java.util.concurrent.Semaphore;Semaphore name=new Semaphore(n); name.a ...
- ASP.NET-A low-level Look at the ASP.NE
请求处理模型1: ******** 1.浏览器向服务器发送请求,先到达服务器的http.sys系统文件,进行初步的处理. (服务器分为内核模式和用户模式,http.sys在内核模式种,IIS在用户模式 ...
- 用101000张图片实现图像识别(算法的实现和流程)-python-tensorflow框架
一个月前,我将kaggle里面的food-101(101000张食物图片),数据包下载下来,想着实现图像识别,做了很长时间,然后自己电脑也带不动,不过好在是最后找各种方法实现出了识别,但是准确率真的非 ...
- Linux I2C核心、总线和设备驱动
目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...
- Linux学习(三)-Vi和Vim的区别
它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面.vim的这些优势主要体现在以下几个方面:1.多级撤消我们知道在vi里,按 u只能撤消上次命 ...