简介

LinkedList是一个双向线性链表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

UML关系图

使用示例

LinkedList list = new LinkedList<>();
//新增
list.add("a");
list.add("a");
list.add("b");
System.out.println(list); //删除
list.remove("a");//删除第一个对应对象
list.remove(0);//删除下标为0的元素
list.removeFirst();//删除第一个元素
list.removeLast();//删除最后一个元素
System.out.println(list); //修改
list.set(0,"c");//修改下标为0的元素
System.out.println(list); //插入
list.add(1,"a");//只能在已有元素的前后或中间位置插入
System.out.println(list); //获取
list.get(0);//根据下标获取元素
list.getFirst();//获取第一个元素
list.getLast();//获取最后一个元素 //循环
//普通循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//foreach循环
for(Object o:list){
System.out.println(o);
}
//迭代循环
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}

源码解析

初始化

节点

//链表的核心类-节点
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;
}
}

构造方法

//默认大小
transient int size = 0;
//第一个节点
transient Node<E> first;
//最后一个节点
transient Node<E> last;
//无参构造
public LinkedList() {
}
//带有初始化集合构造
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}

addAll的分析

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;
//如果index==size,说明链表只有一个节点
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;
} //当一个节点的时候last和当前的节点相同
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
} //更新链表的长度
size += numNew;
modCount++;
return true;
}

常用函数分析

增加

//增加元素,默认向链表的结尾增加节点
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
//获取链表尾部节点
final Node<E> l = last;
//创建新的节点,将最后的节点做为新节点的上一个节点引用,元素为传入元素
final Node<E> newNode = new Node<>(l, e, null);
//链表尾部指向最新节点
last = newNode;
//如果原来尾部节点为空,说明为空链表需要设置链表头部节点
//否则将原来链表尾部节点的next指向新的节点
if (l == null)
first = newNode;
else
l.next = newNode;
//增加大小
size++;
//记录修改次数,主要用于集合迭代,保证迭代数据的准确性
//在迭代时候如果集合发生变化则会抛出异常
//throw new ConcurrentModificationException();
modCount++;
}

删除

//根据下标进行删除节点
public E remove(int index) {
//检测下标是否越界
checkElementIndex(index);
return unlink(node(index));
}
//获取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;
}
} //删除第一个节点
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
//断开第一个节点
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
//将当前节点的下个节点设为第一个节点
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
} //删除最后一个节点
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
//断开最后一个节点
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
//将最后一个节点设为当前节点的上一个节点
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}

修改(替换)元素

//修改某个元素
public E set(int index, E element) {
//检测下标是否越界
checkElementIndex(index);
//根据下标获取节点
Node<E> x = node(index);
//获取节点的元素
E oldVal = x.item;
//赋值新的元素
x.item = element;
return oldVal;
}

插入元素

//插入指定位置元素
public void add(int index, E element) {
//检测下标是否越界
checkPositionIndex(index);
//判断下标是否为最后一个位置,如果是直接插入到最后一个节点
//否则查询出当前index的节点,将新的节点放到原来index节点之前
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
//插入index节点之前位置
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}

获取元素

//根据下标获取元素
public E get(int index) {
//检测下标是否越界
checkElementIndex(index);
//根据下标获取节点,然后返回节点的元素
return node(index).item;
} //获取第一个元素
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
} //获取最后一个元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}

清除所有数据

public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
//将所有数据进行逐个赋值为null,帮助gc的垃圾回收
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
//记录清除的操作,保证迭代的准确性
modCount++;
}

总结

  • 优点
    1、插入和移除数据效率高 2、链表形式方便做为栈或队列场景使用 3、相对arraylist来说,linkedlist无需扩容
  • 缺点
    1、根据随机获取元素效率低(根据下标获取)
    2、批量添加集合效率低

其他

在arraylist和linkedlist中的全局标量都出现了一个修饰符transient,此修饰符的属性默认是不能够被序列化的,jdk作者为了减少空间的浪费所以实现了writeObject(ObjectOutputStream)和readObject(ObjectInputStream)这两个方法,进行手动的序列化有存储数据的有用属性。

参考

更多请关注微信………

LinkedList原理及源码解析的更多相关文章

  1. Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

  3. Spring-Session实现Session共享实现原理以及源码解析

    知其然,还要知其所以然 ! 本篇介绍Spring-Session的整个实现的原理.以及对核心的源码进行简单的介绍! 实现原理介绍 实现原理这里简单说明描述: 就是当Web服务器接收到http请求后,当 ...

  4. Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析

    1.HandlerMapping实现原理及源码解析 在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求U ...

  5. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  6. 【转】Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  7. 有关LinkedList常用方法的源码解析

    上文里解析了有关ArrayList中的几个常用方法的源码——<有关ArrayList常用方法的源码解析>,本文将对LinkedList的常用方法做简要解析. LinkedList是基于链表 ...

  8. Redux异步解决方案之Redux-Thunk原理及源码解析

    前段时间,我们写了一篇Redux源码分析的文章,也分析了跟React连接的库React-Redux的源码实现.但是在Redux的生态中还有一个很重要的部分没有涉及到,那就是Redux的异步解决方案.本 ...

  9. ORB原理与源码解析

    转载: http://blog.csdn.net/luoshixian099/article/details/48523267 CSDN-勿在浮沙筑高台 没有时间重新复制代码,只能一股脑的复制,所以代 ...

随机推荐

  1. 实时语音视频技术webrtc的编译总结

    webrtc编译教程 一.安装depot_tools工具 首先你的电脑上安装了git 1)    下载depot_tools cd到下载的目录下 git clone https://chromium. ...

  2. maven如何修改本地仓库与中央仓库

    摘要: 运行Maven的时候,Maven所需要的任何构件都是直接从本地仓库获取的.如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件. 什么是Maven仓库 在不用M ...

  3. 2016.02.01日,UdoOS系统项目正式开通了

    2016.02.01日,UdoOS系统项目正式开通了,源代码即将开放 Copyright (c) 2016

  4. Ajax请求汇总(一)

    刚开始结束Ajax请求的时候,那真的是迷迷糊糊,昏天暗地,通过学习的深入和翻阅各种资料.求助度娘,总结一下Ajax请求,与大家分享一下,希望能给学习Ajax的同学一些帮助,废话不多手,直接开始~~~ ...

  5. 测试工具——JMeter

    本学期新学的课程,软件测试,上机的实验用到了C++Test,QTP,还有JMeter.今天针对JMeter做一次总结,方便以后用到,知道步骤会更加方便. 首先,对Jmeter进行一个大致的了解,包括对 ...

  6. Python爬知乎妹子都爱取啥名

    闲来无事上知乎,看到好多妹子,于是抓取一波. 有没有兴趣?? 目标网址https://www.zhihu.com/collection/78172986 抓取分析 爬取分析 使用pandas操作文件 ...

  7. 深度解析 H.265 视频解决方案

    又拍云上线了 H.265 从编码解码到 CDN 分发,完整的端到端的自适应解决方案:提供视频上传.视频存储.视频编码.视频分发适配.视频解码等功能.又拍云希望能以云服务的方式将大公司才能长期支付使用的 ...

  8. axis调用webservice的简单方法

    package com.service; import org.apache.axis.client.Call; import org.apache.axis.client.Service; publ ...

  9. Ceph Object Gateway Admin api 获取用户列表问题

    按照官方文档使用Admin Ops API 获取用户列表 GET /admin/user时 返回{code: 403, message: Forbidden}这里有两个问题:首先用户列表的请求为 如下 ...

  10. 基于C#的Appium自动化测试框架(Ⅰ)

    因为工作原因,使用的编程语言都是C#,但是国内相应的Appium资料少得可怜,Java版本的Appium也考虑过,但是奈何自己搞不定Eclipse这个编译环境[说白了就是因为懒-- 无意中看到了外面的 ...