LinkedList 真的是查找慢增删快?刷新你的认知!
作者:dearKundy
链接:blog.csdn.net/dearKundy/article/details/84663512
测试结果废话不多说,先上测试结果。作者分别在 ArrayList 和 LinkedList 的头部、尾部和中间三个位置插入与查找 100000 个元素所消耗的时间来进行对比测试,下面是测试结果。
在这里说明一下测试的环境,尾部插入是在空表的基础上测试的,头部和中间位置插入是在已存在 100000 个元素的表上进行测试的。
测试结论
ArrayList 的查找性能绝对是一流的,无论查询的是哪个位置的元素。
ArrayList 除了尾部插入的性能较好外(位置越靠后性能越好),其他位置性能就不如人意了。
LinkedList 在头尾查找、插入性能都是很棒的,但是在中间位置进行操作的话,性能就差很远了,而且跟 ArrayList 完全不是一个量级的。
源码分析
我们把 Java 中的 ArrayList 和 LinkedList 就是分别对顺序表和双向链表的一种实现,所以在进行源码分析之前,我们先来简单回顾一下数据结构中的顺序表与双向链表中的关键概念。
顺序表:需要申请连续的内存空间保存元素,可以通过内存中的物理位置直接找到元素的逻辑位置。在顺序表中间插入 or 删除元素需要把该元素之后的所有元素向前 or 向后移动。
双向链表:不需要申请连续的内存空间保存元素,需要通过元素的头尾指针找到前继与后继元素(查找元素的时候需要从头 or 尾开始遍历整个链表,直到找到目标元素)。在双向链表中插入 or 删除元素不需要移动元素,只需要改变相关元素的头尾指针即可。
所以我们潜意识会认为:ArrayList 查找快,增删慢。LinkedList 查找慢,增删快。但实际上真的是这样的吗?我们一起来看看吧。
测试程序
测试程序代码基本没有什么营养,这里就不贴出来了,但是得把程序的运行结果贴出来,方便逐个分析。点击这里了解初始 List 的几种方式,关注公众号互联网架构师回复2T获取更多Java系列教程。
运行结果
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; // 创建一个新的结点,其头结点指向last final Node<E> newNode = new Node<>(l, e, null); // 尾结点置为newNode last = 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); else linkBefore(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; else pred.next = newNode; size++; modCount++;
}
总结
对于 LinkedList 来说,头部插入和尾部插入时间复杂度都是 O(1)。
但是对于ArrayList来说,头部的每一次插入都需要移动 size-1 个元素,效率可想而知。
但是如果都是在最中间的位置插入的话,ArrayList 速度比 LinkedList 的速度快将近 10 倍。
ArrayList、LinkedList 查找
这就没啥好说的了,对于 ArrayList,无论什么位置,都是直接通过索引定位到元素,时间复杂度 O(1)。
而对于 LinkedList 查找,其核心方法就是上面所说的 node() 方法,所以头尾查找速度极快,越往中间靠拢效率越低。
关微信公众号:互联网架构师,在后台回复:2T,可以获取我整理的教程,都是干货。
猜你喜欢
1、GitHub 标星 3.2w!史上最全技术人员面试手册!FackBook发起和总结
2、如何才能成为优秀的架构师?
3、从零开始搭建创业公司后台技术栈
4、程序员一般可以从什么平台接私活?
5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...
6、滴滴业务中台构建实践,首次曝光
7、不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事
8、15张图看懂瞎忙和高效的区别
9、2T架构师学习资料干货分享
LinkedList 真的是查找慢增删快?刷新你的认知!相关推荐
- java linkedlist 查找_Java中LinkedList真的是查找慢增删快
测试结果 废话不多说,先上测试结果.作者分别在ArrayList和LinkedList的头部.尾部和中间三个位置插入与查找100000个元素所消耗的时间来进行对比测试,下面是测试结果 (感谢@Hosa ...
- LinkedList 真的是查找慢增删快?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源:juejin.im/post/5c00987de5 ...
- 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 ...
- 实验吧决斗场刷新刷新快刷新
这个题也是个数据隐写的题,按照前面的思路走一遍.先用记事本打开没有什么信息,然后用binwalk打开, 除了一张图片什么都没有,这个就尴尬了,于是我陷入了深深的思索中,但是还是没有什么头绪...... ...
- 经验分享:创业本无捷径,但是选对路真的能让你更快赚到钱
大家好,我是新一 很多人说创业根本就没有什么捷径可走,只有踏踏实实的才能赚到钱. 当然,这话说得没有错. 但是创业又不同于打工,也不是你踏踏实实就一定能赚到钱的. 有的时候看似不起眼的选择,其实就代表 ...
- LinkedList为什么增删快、查询慢
List家族中共两个常用的对象ArrayList和LinkedList,具有以下基本特征. ArrayList:长于随机访问元素,中间插入和移除元素比较慢,在插入时,必须创建空间并将它的所有引用向前移 ...
- 技术随笔 查找速度最快的Google IP
转:http://www.xiumu.org/technology/the-find-the-fastest-in-the-google-ip.shtml 体验秒开GOOGLE的感觉! 在http:/ ...
最新文章
- 华为搜索引擎面世:用不了谷歌,试试「花瓣搜索」?
- c语言运行时显示内存不足,请问:c或c++运行时 遇到虚拟内存不足时咋办,帮优化下代码...
- 【颠覆认知】为什么YouTube广告只看五秒更赚钱,微博商业产品经理深度剖析。...
- (mysql) EXPLAIN语法
- MFC 之 重绘按键Cbutton
- 基于JAVA+Servlet+JSP+MYSQL的在线购物系统
- 利用jasperreports报表生成pdf文档中文不能显示问题解决方法
- java system datetime_Java8新特性时间日期库DateTime API及示例
- 2014-VGG网络讲解
- soapui连接oracle,myeclipse 安装soapui插件
- Java实现附近地点搜索
- Go语言自学系列 | go常用命令
- xpath的常见操作
- WPF 委托和事件实现子窗口回调函数, 实时刷新主窗口控件
- AIML标签中srai不起作用的原因
- OpenMP 快速入门
- 如何使用git 生成密钥?
- TechCrunch Disrupt SF 来啦!快和小探看看本届都有哪些亮点?
- python在txt文件末尾追加写入_在Python文件末尾添加什么?
- 【矩阵论】5. 线性空间与线性变换——生成子空间