ArrayList 与 LinkedList 的区别

ArrayList 与 LinkedList 均是常用的数据结构,但是二者也存在部分区别。

底层数据结构

ArrayList 的底层数据结构是数组,LinkedList 的数据结构是双向链表,这也就表示:ArrayList 所占的内存空间是连续的,LinkedList 是不连续的。

具体内存分布如下图所示:

随机访问速度(根据下标进行访问)

当使用下标访问获取元素时,由于 ArrayList 底层是数据,那么知道了第一个元素的地址,就很容易计算出后面若干个元素的地址,使用下标访问的速度极快。

但是由于 LinkedList 底层是双向链表,元素与元素之间的地址并不连续,没有规律,这时就需要从首元素开始,一个一个遍历,从而获取到指定下标的元素。

相比之下,根据下标进行访问时,ArrayList 的访问速度比 LinkedList 的访问速度更快。

若是查找某个元素是否存在,那么这两个数据结构的查询速度是一样的,均为 o(n)。

增删性能

头插

  • 向 ArrayList 头部插入元素时,由于需要将 ArrayList 中所有的元素向后复制一次,所消耗的时间较多。
  • 向 LinkedList 头部插入元素时,只需要新增一个节点,改变原来头结点与新节点的指针指向,所以头插性能较高。

尾插

向 ArrayList 最后一位插入元素时,只需要新增一个元素,不涉及到元素的复制,因此性能较高。(此处不讨论 ArrayList 的扩容情况)。

向 LinkedList 最后一位插入元素时,与其头插相同,性能较高。

此时可能会有疑问,LinkedList 进行尾插时,不需要遍历一遍找到尾部元素再进行插入错误吗?

针对上面的问题,源码中有解释:

java 复制代码
    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;

其中,last 节点指向了 LinkedList 的最后一个元素,因此,在进行尾插时,只需要新增一个节点并改变 last 等指针的指向即可,性能较高。

中间插

向 ArrayList 中间插入元素时,由于涉及到元素的复制,性能就会下降,并且插入的位置越靠近头部,需要复制的元素就越多,性能下降的就越多。

向 LinkedList 中间插入元素时,需要先对 LinkedList 进行遍历,找到需要插入的位置,然后进行元素的插入,虽然不涉及元素的复制,但是遍历也需要消耗一定的时间,因此性能也较低。

内存占用

由于 ArrayList 的只需要存储元素本身,其内存占用较小。但是 LinkedList 不仅仅需要存储元素本身,还需要存储每个节点的 pre 指针与 next 指针,其占用的内存较大。

关于局部性原理的应用

什么是局部性原理

当我们需要计算 1 + 2 时,就需要 CPU 从内存中读取 1 和 2,但是虽然 CPU 读取内存中的数据很快,但是与计算速度相比,还是比较慢,这时就可以将 1 和 2 先存入 CPU 缓存中,这样 CPU 就可以直接从缓存中读取这两个数据,速度较快,计算完成后,将得到的结果写入缓存中,并在后面某个时间将结果写入内存中。

虽然在将 1 和 2 写入 CPU 缓存中,也会消耗一定的时间,但是由于后面可能还会使用到这两个数据,并且使用时只需要在 CPU 缓存中进行读取,就相当于只是第一次消耗的较多的时间,后面所消耗的时间就非常少了,可以提升计算效率。

于是,对于 ArrayList 就可以使用上面的局部性原理。

应用局部性原理

当我们访问 ArrayList 中的元素时,由于访问了第一个元素,就会有很大的可能访问后面几个元素,于是就可以将这几个元素一并存入 CPU 缓存中,这样下次访问时,就能节省一定的时间。并且由于 ArrayList 底层的数据结构是数组,内存分布连续,这样就不会浪费 CPU 缓存中的内存。

如下图所示:

当 CPU 缓存中的空间满时,就会淘汰掉最先加进来的元素。

但是,LinkedList 无法应用局部性原理,因为 LinkedList 的底层数据结构是双向链表,内存分布不连续,这样即使将一部分元素加入到 CPU 缓存中,也会浪费缓存中的空间,这样一来,CPU 缓存中只有少部分存入了有效元素,剩下的大部分空间均被浪费了。

如下图所示:

相关推荐
sheji34161 分钟前
【开题答辩全过程】以 环保监督管理系统为例,包含答辩的问题和答案
java·eclipse
不会玩电脑的Xin.4 分钟前
Web请求乱码解决方案
java·javaweb
没书读了5 分钟前
计算机组成原理-考前记忆清单
线性代数·算法
Billow_lamb13 分钟前
Spring Boot2.x.x 全局错误处理
java·spring boot·后端
编程火箭车17 分钟前
【Java SE 基础学习打卡】14 Java 注释
java·编程规范·代码注释·api文档·代码可读性·javadoc·文档注释
雁于飞25 分钟前
分布式基础
java·spring boot·分布式·spring·wpf·cloud native
Hcoco_me33 分钟前
大模型面试题5:矩阵(M*M)特征值分解的步骤
算法·机器学习·矩阵
q***25141 分钟前
java进阶1——JVM
java·开发语言·jvm
while(1){yan}42 分钟前
线程的状态
java·开发语言·jvm
豐儀麟阁贵1 小时前
8.3 Java常见的异常类
java·开发语言