之前写了一篇ArrayList,那么今天就写一篇他的姊妹篇,LinkedList。

众所周知,ArrayList底层数据是数组,可以在O(1)的时间内get到数据,但删除和插入就要O(n)时间复杂度。

所以出现了链表,链表可以在O(1)的时间内插入,并且不会浪费内存,用多少就链接多少即可。

我们从以下几个方面介绍LinkedList

  • Node节点
  • add方法
  • remove方法
  • get方法

(一)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;
}
}

我们可以看出每个结点的组成部分有三个,一个是item数据,一个是prev前驱节点,一个是next后驱节点。

那么就可以知道LinkedList就是一个双向链表,每个节点既有指向后面的链表,也有指向前面的链表。如下图(画的不好,见谅)

(二)add方法

 //最基本的add方法,其他方法都是这个方法的变体
public boolean add(E e) {
linkLast(e);
return true;
}

直接调用了linkLast方法(也就是说,add方法是默认插入到链表的尾端),然后return 一个 true。

 void linkLast(E e) {
//将链表的last节点给l
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++;
}

我们知道add方法是在队列尾部添加元素,还是很容易的。首先用变量 l 指向最后一个节点,然后创建一个节点将它的prev指向 l ,这样newnode成为最后一个节点,使用last指向它,接着使 l 的next指向newnode,这种直接添加在队列尾部的方式还是很好理解的,我们重点看看如何添加在队列的中间位置。

 public void add(int index, E element) {
//检查插入位置是否合法
checkPositionIndex(index); //如果插入到最后,直接调用linkLast方法
if (index == size)
linkLast(element);
//否则调用linkBefore
else
linkBefore(element, node(index));
}

直接看注释。在调用linkBefore之前,调用了node(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;
}
}

首先判断在前半部分还是在后半部分,然后一个for循环查找。时间复杂度O(n), 没办法,链表的缺点。

 void linkBefore(E e, Node<E> succ) {
// assert succ != null;
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++;
}

(三)remove方法

看完了添加,删除就显得简单些,无非分为两种,从头部删除,从中间删除,从头部删除和从尾部添加一样简单,从中间删除就是把此结点的前一个结点的next指向此结点的后一个结点,并把后一个结点的prev指向此节点的前一个结点,就是跳过此结点,最终将此结点null交给GC大人解决。为了篇幅,我们不再赘述。

(四)get方法

由于LinkedList是链表,get方法必须扫描一遍链表,效率极低,所以谨慎使用。

  public E get(int index) {
//检查是否超过链表长度或者负数
checkElementIndex(index);
//node节点我前面分析过了,O(n)复杂度
return node(index).item;
}

从源代码中我们可以清晰的看到,所谓的get方法也就是,调用node方法遍历整个链表,只是其中稍微做了点优化,如果index的值小于size/2从头部遍历,否则从尾部遍历。可见效率一样低下,所以我们以后写程序的时候,如果遇到数据量不大但是需要经常遍历查找的时候使用ArrayList而不是LinkedList,如果数据量非常的大,但是不是很经常的查找时使用LinkedList。

基于JDK1.8的LinkedList剖析的更多相关文章

  1. 基于JDK1.8的LinkedList源码学习笔记

    LinkedList作为一种常用的List,是除了ArrayList之外最有用的List.其同样实现了List接口,但是除此之外它同样实现了Deque接口,而Deque是一个双端队列接口,其继承自Qu ...

  2. Java集合基于JDK1.8的LinkedList源码分析

    上篇我们分析了ArrayList的底层实现,知道了ArrayList底层是基于数组实现的,因此具有查找修改快而插入删除慢的特点.本篇介绍的LinkedList是List接口的另一种实现,它的底层是基于 ...

  3. 基于JDK1.8的ArrayList剖析

    前言 本文是基于JDK1.8的ArrayList进行分析的.本文大概从以下几个方面来分析ArrayList这个数据结构 构造方法 add方法 扩容 remove方法 (一)构造方法 /** * Con ...

  4. ConcurrentHashMap基于JDK1.8源码剖析

    前言 声明,本文用的是jdk1.8 前面章节回顾: Collection总览 List集合就这么简单[源码剖析] Map集合.散列表.红黑树介绍 HashMap就是这么简单[源码剖析] LinkedH ...

  5. Java -- 基于JDK1.8的LinkedList源码分析

    1,上周末我们一起分析了ArrayList的源码并进行了一些总结,因为最近在看Collection这一块的东西,下面的图也是大致的总结了Collection里面重要的接口和类,如果没有意外的话后面基本 ...

  6. Java集合类源码解析:HashMap (基于JDK1.8)

    目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...

  7. Java 集合 JDK1.7的LinkedList

    Java 集合 JDK1.7的LinkedList @author ixenos LinkedList LinkedList是List接口的双向链表实现,JDK1.7以前是双向循环链表,以后是双向非循 ...

  8. 基于JDK1.8的HashMap分析

    HashMap的强大功能,相信大家都了解一二.之前看过HashMap的源代码,都是基于JDK1.6的,并且知其然不知其所以然,现在趁着寒假有时间,温故而知新.文章大概有以下几个方面: HashMap的 ...

  9. 基于JDK1.8版本的hashmap源码笔记(二)

    这一篇是接着上一篇写的, 上一篇的地址是:基于JDK1.8版本的hashmap源码分析(一)     /**     * 返回boolean类型的值,当集合中包含key的键值,就返回true,否则就返 ...

随机推荐

  1. 深入理解final关键字以及一些建议

    引子:一说到final关键字,相信大家都会立刻想起一些基本的作用,那么我们先稍微用寥寥数行来回顾一下. 一.final关键字的含义 final是Java中的一个保留关键字,它可以标记在成员变量.方法. ...

  2. FORTH运算符

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...

  3. Laravel的unique和exists验证规则的优化

    本文是Laravel实战:任务管理系统(一)的扩展阅读 原文链接:http://pilishen.com/posts/Improvements-to-the-Laravel-unique-and-ex ...

  4. crontab执行带参数的php脚本,并取得参数[转]

    现在越来越喜欢用linux了,程序当中也去掉了很多触发性判断,改用了借用linux的crontab的特性来进行,这样程序效率确实是高了很多. 比如我们每月1号清空月点击,比如每天凌晨统计上一天的访问报 ...

  5. JAVA中JavaBean对象之间拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,有个更简便的方法,他们之间的拷贝可以通过copyPro ...

  6. wamp图标为黄色(非端口号问题)

    1.Win键+R 输入:services.msc  进入服务,找到wamp,看哪个服务没有启动 2.手动启动apache服务失败,弹出以下错误 3.然后在cmd命令行中切换到你的apache的bin目 ...

  7. CentOS 通过yum来升级php到php5.6

    在文章中,我们将展示在centOS系统下如果将php升级到5.6,之前通过yum来安装lamp环境,直接升级的话,提示没有更新包,也就是说默认情况下php5.3.3是最新 1.查看已经安装的php版本 ...

  8. 浅谈python lambda

    lambda x: x * x,实际上就是 def f(x): retrun x * x 关键字lambda表示匿名函数,冒号前的x表示函数参数. 匿名函数只能有一个表达式,不用写return,返回值 ...

  9. Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  10. 【Java入门提高篇】Day13 Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...