0. 前言

先对LinkedList的特性进行一个概述: 
(1)LinkedList底层实现为双向循环链表。链表的特点就是插入删除数据快,而查询数据慢。

(2)因为使用链表的原因,所以不存在容量不足的问题,没有扩容机制。

(3)从后面的源码分析中我们也可以看出,LinkedList支持null并且LinkedList没有同步机制。

(4)LinkedList直接继承于AbstractSequentialList,同时实现了List接口,也实现了Deque接口。

AbstractSequentialList为顺序访问的数据存储结构提供了一个骨架类实现,如果要支持随机访问,则优先选择AbstractList类继承。LinkedList 基于链表实现,因此它继承了AbstractSequentialList。本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/53012654

1.  LinkedList数据存储格式

上面也提到了,LinkedList底层实现为双向链表,下面是某个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;
}
}

下面我们分析一个LinkedList的add()操作,可以帮助我们理解LinkedList的双向链表的实现原理。


2.  LinkedList的add操作

public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}

上面是根据index进行的LinkedList的add操作,首先会判断index是否合法,再判断是不是将该节点插入到链表的最后,最后才是进行链表的中间插入操作。


2.1    链表尾部add

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++;
}

在linkLast()方法中,首先会生成一个新的Node节点,然后将这个新的末尾节点赋值给last节点,如果l为null,说明是第一次加入数据,就将这个节点置为first节点,代表链表中第一个有数据的节点,否则将上一个节点的next指向这个节点。

说到第一个节点和最后一个节点,LinkedList提供给了我们具体的方法进行这两个特殊位置的数据获取:

//获取第一个元素
System.out.println("第一个元素是:" + list.getFirst());
//获取最后一个元素
System.out.println("最后一个元素是:"+list.getLast());

2.2    链表中间add

中间插入节点调用了linkBefore(element,node(index))方法,那么首先分析一下node(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;
}
}

首先判断index值是不是小于整个链表长度的一半,整个if/else逻辑是在判断要插入的位置是距离链表头近还是链表尾近,目的更快的找到原来index处的节点并返回。接下来就是linkBefore()方法:

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++;
}

pred 是当前要插入位置节点的上一个节点,即图中的第一个节点。newNode 将要插入的对象包装成node节点(指定上一个节点为pred,下一个节点为node()返回值succ)。

接着将succ节点的上一个节点指定为我们的新节点newNode。那么肯定会有逻辑将pred的next设置为newNode。

果然,最后做了一个判断,如果pred为null,说明succ节点为第一个有数据的节点,就将生成的新节点newNode置为first节点,否则指定上个节点的下一个节点为生成的新节点,即pred.next = newNode。

这样就完成了整个链表数据插入过程。显然是不同于ArrayList那样地进行数组复制。

3.  LinkedList的get操作

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
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;
}
}

get操作就比较简单了,对链表进行遍历,直接找到node节点,返回item数据即可。

4.  ArrayList和LinkedList的比较

4.1    ArrayList和LinkedList的相同点

(1)两者均不是线程安全的。

(2)两者都支持null值。

(3)都实现了List接口。

4.2  ArrayList和LinkedList的不同点

(1)LinkedList 基于链表实现,便于顺序访问,它继承了AbstractSequentialList。而ArrayList支持随机访问,继承了AbstractList类。

(2)因为LinkedList 是基于链表的,因此不像ArrayList需要扩容机制。

(3)各种操作的性能对比:对于ArrayList来说,得益于快速随机访问的特性,获取任意位置元素是比较有效率的。如果是add或者remove操作,要分两种情况,如果是在ArrayList尾部做add,是不需要移动其他元素,耗时是O(1)。但如果在中间插入新元素的话,耗时是O(n-index)。另外,当ArrayList扩容时,会自动生成一个新的array(长度是之前的1.5倍),再将旧的array移值上去,耗时是O(n)。为了确保本文正确性,博主会在发现某部分有不妥描述时及时修改,请确保您看到的是原文,原文链接为SEU_Calvin的博客

因此,ArrayList的get操作快一些,而add操作,若add的位置为List中间,肯定是LinkedList要快一些,尾部的话两者差不多。具体使用哪个需要分场景选择最合适的。

最后希望这篇文章对你有所帮助,也希望大家多点赞支持。 ╮( ̄▽ ̄”)╭

Java集合——LinkedList源码详解的更多相关文章

  1. Java集合——TreeMap源码详解

    )TreeMap 是一个有序的key-value集合,它是通过红黑树实现的.因为红黑树是平衡的二叉搜索树,所以其put(包含update操作).get.remove的时间复杂度都为log(n). (2 ...

  2. Java集合——ArrayList源码详解

    ) ArrayList 实现了RandomAccess, Cloneable, java.io.Serializable三个标记接口,表示它自身支持快速随机访问,克隆,序列化. public clas ...

  3. Java集合——LinkedHashMap源码详解

    个KV.LinkedHashMap不仅像HashMap那样对其进行基于哈希表和单链表的Entry数组+ next链表的存储方式,而且还结合了LinkedList的优点,为每个Entry节点增加了前驱和 ...

  4. Java集合---LinkedList源码解析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  5. Java集合-LinkedList源码分析

    目录 1.数据结构-链表 2.ArrayList结构特性 3.构造方法 4.成员变量 5.常用的成员方法 6.Node节点 7.序列化原理 8.迭代器 9.总结 1.数据结构-链表 链表(Linked ...

  6. java 基础数据结构源码详解及数据结构算法

    http://www.cnblogs.com/skywang12345/category/455711.html http://www.cnblogs.com/liqiu/p/3302607.html

  7. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  8. 【java集合框架源码剖析系列】java源码剖析之LinkedList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 在实际项目中LinkedList也是使用频率非常高的一种集合,本博客将从源码角度带领大家学习关于LinkedList的知识. ...

  9. Java源码详解系列(十)--全面分析mybatis的使用、源码和代码生成器(总计5篇博客)

    简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository ...

随机推荐

  1. 深入理解 iOS Rendering Process

    本文将从 OpenGL 的角度结合 Apple 官方给出的部分资料,介绍 iOS Rendering Process 的概念及其整个底层渲染管道的各个流程. 相信在理解了 iOS Rendering ...

  2. 【JavaScript】颜色选择器

    颜色空间RGB与HSV(HSL)的转换 好文推荐:http://blog.csdn.net/jiangxinyu/article/details/8000999 从 HSV 到 RGB 的转换 类似的 ...

  3. Kali-linux物理访问攻击

    物理访问攻击与提升用户的权限类似.即当一个普通用户登录到系统中,破解本地其他用户账户的密码.在Linux中,普通用户可以通过su命令代替其他用户执行某些操作,意味着该用户能够在Linux/Unix系统 ...

  4. logback将日志写入不同文件夹里

    转载:logback不同业务的日志打印到不同文件 一.logback.xml文件配置如下: <?xml version="1.0" encoding="UTF-8& ...

  5. jQuery.mobile.changePage的参数

    选项 类型:对象 属性: allowSamePageTransition(默认值:假的) 类型:布尔 默认情况下,changePage()忽略请求更改为当前活动页面.将此选项设置为true,则允许该请 ...

  6. ubuntu查询命令行安装的软件的安装路径

    which git // 查询git的安装路径

  7. [LuoguP1363]幻想迷宫

    [LuoguP1363]幻想迷宫(Link) 现在有一个迷宫,从迷宫边界的任意一点可以走到对面,即:若都是路面,则可以从\((1, i)\)走到\((N, i)\).其余情况依旧.问是否可以从指定的起 ...

  8. Luogu[P1108] 低价购买

    \(Link\) \(\mathcal{\color{red}{Description}}\) 请你求出一个数列的最长下降子序列长度,并为此求出其方案数. \[1 \leq N \leq 5000\] ...

  9. c#中的结构

    1.在c#中,结构是值类型的数据结构,它可以使用一个单一的变量存储各种数据类型的相关数据,使用Struct关键字进行声明. 2.C#中结构的特点: (1)结构中可以有字段,属性,方法,运算表达式,事件 ...

  10. oo第二次总结作业

    OO电梯作业总结 这三周的作业和课堂内容以及OS的课上内容都相同,都是关于多线程方面的知识.在这次作业中由浅入深布置了三项多线程电梯方面的作业,让我们在实践中感受了多线程的工作原理以及各项需要注意的要 ...