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 缓存中只有少部分存入了有效元素,剩下的大部分空间均被浪费了。

如下图所示:

相关推荐
天天进步20153 分钟前
KrillinAI 源码级深度拆解二:时间轴的艺术:深入 KrillinAI 的字幕对齐与音频切分算法
算法·音视频
信码由缰3 分钟前
Java 中的 AI 与机器学习:TensorFlow、DJL 与企业级 AI
java
爱编程的小吴4 分钟前
【力扣练习题】121. 买卖股票的最佳时机
算法·leetcode·职场和发展
生信大杂烩6 分钟前
空间转录组分析新工具 | MEcell:自适应微环境感知建模,精准解析细胞身份!
算法·数据分析
kaikaile199512 分钟前
计算向量x的功率谱密度
算法
ADI_OP17 分钟前
ADAU1452的开发教程3:常规音频算法的开发(1)
算法·音视频·adi dsp中文资料·adi dsp开发教程
꧁Q༒ོγ꧂22 分钟前
算法详解(三)--递归与分治
开发语言·c++·算法·排序算法
沙子迷了蜗牛眼26 分钟前
当展示列表使用 URL.createObjectURL 的创建临时图片、视频无法加载问题
java·前端·javascript·vue.js
ganshenml28 分钟前
【Android】 开发四角版本全解析:AS、AGP、Gradle 与 JDK 的配套关系
android·java·开发语言
我命由我1234529 分钟前
Kotlin 运算符 - == 运算符与 === 运算符
android·java·开发语言·java-ee·kotlin·android studio·android-studio