LinkedList 真的是查找慢增删快?
点击上方蓝色“程序猿DD”,选择“设为星标”
回复“资源”获取独家整理的学习资料!
来源:juejin.im/post/5c00987de51d451aa843b67b
测试结果
废话不多说,先上测试结果。作者分别在ArrayList和LinkedList的头部、尾部和中间三个位置插入与查找100000个元素所消耗的时间来进行对比测试,下面是测试结果
在这里说明一下测试的环境,尾部插入是在空表的基础上测试的,头部和中间位置插入是在已存在100000个元素的表上进行测试的
测试结论
ArrayList的查找性能绝对是一流的,无论查询的是哪个位置的元素
ArrayList除了尾部插入的性能较好外(位置越靠后性能越好),其他位置性能就不如人意了
LinkedList在头尾查找、插入性能都是很棒的,但是在中间位置进行操作的话,性能就差很远了,而且跟ArrayList完全不是一个量级的
源码分析
我们把Java中的ArrayList和LinkedList就是分别对顺序表和双向链表的一种实现,所以在进行源码分析之前,我们先来简单回顾一下数据结构中的顺序表与双向链表中的关键概念
顺序表: 需要申请连续的内存空间保存元素,可以通过内存中的物理位置直接找到元素的逻辑位置。在顺序表中间插入or删除元素需要把该元素之后的所有元素向前or向后移动。
双向链表: 不需要申请连续的内存空间保存元素,需要通过元素的头尾指针找到前继与后继元素(查找元素的时候需要从头or尾开始遍历整个链表,直到找到目标元素)。在双向链表中插入or删除元素不需要移动元素,只需要改变相关元素的头尾指针即可。
所以我们潜意识会认为:ArrayList查找快,增删慢。LinkedList查找慢,增删快。但实际上真的是这样的吗?我们一起来看看吧。
测试程序
测试程序代码基本没有什么营养,这里就不贴出来了,但是得把程序的运行结果贴出来,方便逐个分析。
运行结果
ArrayList尾部插入100000个元素耗时:26ms
LinkedList尾部插入100000个元素耗时:28ms
ArrayList头部插入100000个元素耗时:859ms
LinkedList头部插入100000个元素耗时:15ms
ArrayList中间插入100000个元素耗时:1848ms
LinkedList中间插入100000个元素耗时:15981ms
ArrayList头部读取100000个元素耗时:7ms
LinkedList头部读取100000个元素耗时:11ms
ArrayList尾部读取100000个元素耗时:12ms
LinkedList尾部读取100000个元素耗时:9ms
ArrayList中间读取100000个元素耗时:13ms
LinkedList中间读取100000个元素耗时:11387ms
ArrayList尾部插入
add(E e)
方法
public boolean add(E e) {// 检查是否需要扩容ensureCapacityInternal(size + 1); // Increments modCount!!// 直接在尾部添加元素elementData[size++] = e;return true;}
可以看出,对ArrayList的尾部插入,直接插入即可,无须额外的操作。
LinkedList尾部插入
LinkedList中定义了头尾节点
/*** Pointer to first node.*/transient Node<E> first;/*** Pointer to last node.*/transient Node<E> last;
add(E e)
方法,该方法中调用了linkLast(E e)
方法
public boolean add(E e) {linkLast(e);return true;}
linkLast(E e)方法,可以看出,在尾部插入的时候,并不需要从头开始遍历整个链表,因为已经事先保存了尾结点,所以可以直接在尾结点后面插入元素
/*** Links e as last element.*/void linkLast(E e) {// 先把原来的尾结点保存下来final Node<E> l = last;// 创建一个新的结点,其头结点指向lastfinal Node<E> newNode = new Node<>(l, e, null);// 尾结点置为newNodelast = newNode;if (l == null)first = newNode;else// 修改原先的尾结点的尾结点,使其指向新的尾结点l.next = newNode;size++;modCount++;}
总结
对于尾部插入而言,ArrayList与LinkedList的性能几乎是一致的
ArrayList头部插入
add(int index, E element)
方法,可以看到通过调用系统的数组复制方法来实现了元素的移动。所以,插入的位置越靠前,需要移动的元素就会越多
public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size + 1); // Increments modCount!!// 把原来数组中的index位置开始的元素全部复制到index+1开始的位置(其实就是index后面的元素向后移动一位)System.arraycopy(elementData, index, elementData, index + 1,size - index);// 插入元素elementData[index] = element;size++;}
LinkedList头部插入
add(int index, E element)
方法,该方法先判断是否是在尾部插入,如果是调用linkLast()
方法,否则调用linkBefore()
,那么是否真的就是需要重头开始遍历呢?我们一起来看看
public void add(int index, E element) {checkPositionIndex(index);if (index == size)linkLast(element);elselinkBefore(element, node(index));}
在头尾以外的位置插入元素当然得找出这个位置在哪里,这里面的node()方法就是关键所在,这个函数的作用就是根据索引查找元素,但是它会先判断index的位置,如果index比size的一半(size >> 1,右移运算,相当于除以2)要小,就从头开始遍历。
否则,从尾部开始遍历。从而可以知道,对于LinkedList来说,操作的元素的位置越往中间靠拢,效率就越低
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;}}
这个函数的工作就只是负责把元素插入到相应的位置而已,关键的工作在node()方法中已经完成了
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;elsepred.next = newNode;size++;modCount++;}
总结
对于LinkedList来说,头部插入和尾部插入时间复杂度都是O(1)
但是对于ArrayList来说,头部的每一次插入都需要移动size-1个元素,效率可想而知
但是如果都是在最中间的位置插入的话,ArrayList速度比LinkedList的速度快将近10倍
ArrayList、LinkedList查找
这就没啥好说的了,对于ArrayList,无论什么位置,都是直接通过索引定位到元素,时间复杂度O(1)
而对于LinkedList查找,其核心方法就是上面所说的node()方法,所以头尾查找速度极快,越往中间靠拢效率越低
往期推荐
一个让你敲代码的同时,找回童年乐趣的 IntelliJ 插件
1024 致敬极客精神,我们有一个3天的秘境邀请!
完美,竟然用一个脚本就把系统升级到https了,且永久免费!
Guava 中的 Stopwatch 是个什么鬼?
这款可视化工具,Java 调优起来真的 so easy啊
扫一扫,关注我
一起学习,一起进步
每周赠书,福利不断
﹀
﹀
﹀
深度内容
推荐加入
最近热门内容回顾 #技术人系列
LinkedList 真的是查找慢增删快?相关推荐
- java linkedlist 查找_Java中LinkedList真的是查找慢增删快
测试结果 废话不多说,先上测试结果.作者分别在ArrayList和LinkedList的头部.尾部和中间三个位置插入与查找100000个元素所消耗的时间来进行对比测试,下面是测试结果 (感谢@Hosa ...
- LinkedList真的比ArrayList增删快吗?为什么?
首先,我们都知道,LinkedList数据结构是链表且是双向链表(每个节点会存储上个节点的地址.存储数据和下个节点的地址): (图略微潦草,将就着看) 而ArrayList的数据存储结构则是数组,数组 ...
- LinkedList插入元素一定比ArrayList快吗
在选择数据结构的时候,我们通常会考虑每种数据结构不同操作的时间复杂度,以及使用场景两个因素. 对于数组,随机元素访问的时间复杂度是 O(1),元素插入操作是 O(n): 对于链表,随机元素访问的时间复 ...
- Linux 命令 find / -ctime +1 真的是查找1天前创建的文件咩?
链接: http://blog.itpub.net/28602568/viewspace-1404761/ 标题: Linux 命令 find / -ctime +1 真的是查找1天前创建的文件咩? ...
- 无锁队列真的比有锁队列快吗【c++ linux后台开发】
无锁队列真的比有锁队列快吗[c++ linux后台开发]|解决内存频繁分配问题|无锁队列没数据可读时怎么休眠|无锁队列到底是不是终极解决方案 专注后台服务器开发,包括C/C++,Linux,Nginx ...
- 经验分享:创业本无捷径,但是选对路真的能让你更快赚到钱
大家好,我是新一 很多人说创业根本就没有什么捷径可走,只有踏踏实实的才能赚到钱. 当然,这话说得没有错. 但是创业又不同于打工,也不是你踏踏实实就一定能赚到钱的. 有的时候看似不起眼的选择,其实就代表 ...
- LinkedList为什么增删快、查询慢
List家族中共两个常用的对象ArrayList和LinkedList,具有以下基本特征. ArrayList:长于随机访问元素,中间插入和移除元素比较慢,在插入时,必须创建空间并将它的所有引用向前移 ...
- 技术随笔 查找速度最快的Google IP
转:http://www.xiumu.org/technology/the-find-the-fastest-in-the-google-ip.shtml 体验秒开GOOGLE的感觉! 在http:/ ...
- ios keychain 不被清理_清理垃圾,恢复出厂设置真的能让手机变快吗?
支付宝每天领红包 打开支付宝首页, 搜索 "9479339",领取支付宝红包,最高99元 搜索"体验金71127639"领百万体验金,收益归你 搜索" ...
最新文章
- 计算机视觉顶尖期刊和会议的段子
- Windows Embedded为航海护航!
- 《剑指offer》-- 构建乘积数组、求1+2+3+...+n、不用加减乘除做加法、包含min函数的栈、用两个栈实现队列
- 2015.7.17( NOI2015 day1 )
- ImportError: cannot import name 'pyopenpose' from 'openpose'错误解决方法
- 用 Python 识别图片中的文字
- Windows10下的docker安装与入门 (一)使用docker toolbox安装docker
- Android中通过ViewHelper.setTranslationY实现View移动控制(NineOldAndroids开源项目)
- 金蝶k3单据编码规则_金蝶K3ERP编码规则.doc
- 安装配置fcitx输入法
- float函数python作用_Python内置函数float()
- 【Flutter 问题系列第 49 篇】在 Flutter 中如何给组件设置背景色、圆角、边框、形状、阴影、渐变色、背景图片等效果
- 老男孩教育学习linux决心书
- 成为一名Java后端工程师需要掌握的技能
- 【我的世界】自定义局域网服务器-LanServerPropertie-1.17.x-自定义端口+关正版验证
- 英国几个有名的英语考试总结
- 腾讯云实时音视频带你玩转语音聊天室
- 查看linux版本是多少位
- C/C++ 余弦函数 cos - C语言零基础入门教程
- Nodejs之解决接口跨域问题
热门文章
- linux ps命令 格式 进程状态 各字段含义 简介
- linux selinux 安全子系统简介
- linux shell 算术运算{expr、bc、dc、(( ))和[ ]}
- linux查看网卡速率
- pkg-config简介 pkgconfig
- 内核模式下的文件操作
- Python学习之猜数字小游戏
- 得到文件的服务器路径,如何获取服务器上的路径?
- oracle中asm磁盘不足,Oracle用户无法访问ASM磁盘组问题
- 希尔排序的java算法_Java算法系列篇 【希尔排序】