java 在数组末尾添加元素_Java快问快答:用 ArrayList 还是 LinkedList?

7812bd7670e0828444d74a47d71b2fdf.png

问题:

通常我会这么定义列表:

List<String> names = new ArrayList<>()

names类型使用List接口,那么具体实现该如何选择。

什么时候应该用LinkedList替代ArrayList,反之亦然?

这里大家可以关注一下我的个人专栏《Java 进阶集中营》,每天会给大家即时分享一个最新的java技术资讯,有优秀的java技术内容,也欢迎分享在我的专栏。

JAVA 进阶集中营​zhuanlan.zhihu.com
286e66b5576bd0eea7f7b798b36bf6f8.png

总结:

大多数情况下,相比LinkedList更推荐使用ArrayList或ArrayDeque。如果不确定,可以直接选用ArrayList。

LinkedList和ArrayList是List接口的两种不同实现。LinkedList采用双向链表实现。ArrayList通过动态调整数组大小实现。

与标准链表和数组操作一样,不同的实现方法算法运行时也不同。

对于LinkedList<E>

  • get(int index)复杂度为O(n)(平均步长n/4)
  • add(E element)复杂度为O(1)
  • add(int index, E element)复杂度为O(n)(平均步长n/4)。但是当index = 0时复杂度为O(1)<--- LinkedList<E>的主要优点。
  • remove(int index)复杂度为O(n)(平均步长n/4)
  • Iterator.remove()复杂度为O(1)。<---LinkedList<E>的主要优点
  • ListIterator.add(E element)复杂度为O(1)。这是LinkedList<E>的一个主要优点。

注意:许多操作平均需要n/4步长,最好的情况下(例如index= 0)步长为常数,最坏的情况下需要n/2步(列表中间)。

对于ArrayList<E>

  • get(int index)复杂度为O(1)<--- ArrayList<E>的主要优点
  • add(E element)分摊后的复杂度为O(1),但最坏的情况是O(n),因为需要调整数组大小并进行拷贝
  • add(int index,E element)复杂度为O(n)(平均步长n/2)
  • remove(int index)复杂度为O(n)(平均步长n/2)
  • Iterator.remove()复杂度为O(n)(平均步长n/2)
  • ListIterator.add(E element)复杂度为O(n)(平均步长n/2)

注意:许多操作要求平均步长为n/2,最好情况下(列表末尾)步长为常数,最坏情况下(列表开始)需要n步

LinkedList<E>可以使用iterator实现固定时间插入或删除,但只能顺序访问元素。换句话说,可以向前或向后遍历列表,但是在列表中查找固定位置元素花费的时间与列表大小成正比。Javadoc中这么写道:“在列表中建立索引,会从列头或列尾开始遍历,从更靠近的位置开始”,这些方法平均复杂度为O(n)(平均步长n/4),尽管index = 0时复杂度为O(1)。

另一方面,ArrayList<E>支持快速随机读取访问,因此获取任何元素都能在恒定时间内完成。但是,除了列尾在其它任何位置添加或删除元素,都需要把后面的所有元素移位。同样,如果添加的元素多于底层数组的容量,则会分配一个新数组(大小是之前的1.5倍),并把旧数组复制到新数组中。因此在ArrayList中添加元素时间复杂度最差为O(n),平均情况下为常数。

因此,根据您打算执行的操作选择对应的实现。遍历这两种List开销都很小。(从技术上看ArrayList更快,但除非确实对性能要求十分敏感,否则不必担心。遍历的复杂度都是常量)

使用LinkedList其中一个好处可以重用已有iterator插入和删除元素。然后修改本地列表即可,操作的时间复杂度为O(1)。在ArrayList中,数组余下的部分需要移动(即拷贝)。而在LinkedList中执行seek操作遍历,最坏时间复杂度为O(n)(平均步长n/2),而ArrayList中,可以直接计算位置进行访问,复杂度为O(1)。

在LinkedList列头增加或删除操作时间复杂度为O(1),而ArrayList需要O(n)。请注意:ArrayDeque可以用来替代LinkedList,适合在列头添加和删除元素,但它不是List。

另外,如果列表很大,请记住,内存使用情况也有所不同。每个LinkedList元素都有额外开销,因为里面还存储了指向下一个和上一个元素的指针。ArrayLists没有这种开销。但是,无论是否实际添加了元素,ArrayList都会分配初始容量大小的内存。

ArrayList默认初始容量很小(Java 1.4-1.8中设为10)。但是由于底层实现是数组,因此如果添加很多元素,则必须调整数组大小。如果提前知道需要添加很多元素,为避免调整数组大小带来的开销,在创建ArrayList时需要设置更大的初始容量。

答案2(案例分析):

现代计算机体系结构中,ArrayList性能几乎在所有情况下都得到大大提升。因此,除非一些非常独特和极端的情况,应避免使用LinkedList。

从理论上讲,LinkedList的add(E element)时间复杂度为O(1)。

同样,在列表中间添加元素应该非常有效。

实际并非如此,因为LinkedList是一种对缓存不友好的数据结构。从性能的角度看,只有在极少数情况下,LinkedList的性能会优于缓存友好的ArrayList。

下面是在随机位置插入元素的基准测试结果。就像你看到的那样:ArrayList的效率要高得多。尽管从理论上讲,每次向列表中插入元素都需要“移动”数组中后续n个后元素(个数越少越好):

3fb59352707ebcd37b51b32d384a6f93.png

在缓存更大、速度更快的下一代硬件上运行,得出的结论更明确:

5fd3a25a9215a4d64111c9f0ec715fa5.png

LinkedList完成相同工作所需的时间更长。

有两个主要原因:

  1. 主要原因:LinkedList节点随机分布在整个内存中。RAM(“随机访问存储器”)并不是真正随机,需要获取内存块进行缓存。这个操作非常耗时,并且当这种操作频繁发生时,需要一直替换缓存中的内存页 -> 缓存未命中 -> 缓存效率低下。ArrayList元素存储在连续内存中,这正是现代CPU架构优化的内容。
  2. 其次,LinkedList需要保留指向前一个与后一个元素的指针,这意味着每个元素的内存消耗是ArrayList的3倍。

DynamicIntArray是一个自定义ArrayList实现,元素类型为Int(原始类型)而非Object。因此所有数据实际上都是相邻存储,因此效率更高。

记住一个关键因素,获取存储块比访问单个存储单元的开销更大。这就是为什么读取器1MB顺序内存要比从不同内存块中读取同样的数据量快400倍的原因:

延迟数据比较(〜2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

来源:每个程序员都应该知道的延迟数据

为了让观点表达得更清晰,可以查看列表开头的add element结果。从理论上讲,这只是一种情况。其实LinkedList应该表现得更好,而ArrayList的结果应该不及它:

ccefd44414b9126b48f0b311471f96a7.png

注意:这是C++ Std库的基准测试。但是根据我之前的经验,C++和Java的结果非常类似。源代码

复制连续内存是现代CPU的一种优化:理论在不断演变,实际上又让ArrayList和Vector效率变得更高。

参考文中发布的所有基准测试均来自KjellHedström。在他的博客上可以找到更多数据。

博客地址:https://kjellkod.wordpress.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/406387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何利用WGET覆写已存在的档案

如果你有用wget下載檔案&#xff0c;而且檔名都是一樣的話&#xff0c;如設定排程每天自動下載antivir的病毒碼&#xff0c;那麼你會發現&#xff0c;wget不會覆寫原本已經存在的檔案。而在他的說明文件中&#xff0c;似乎也沒有辦法在下載前先刪除已存在的檔案&#xff0c;只有…

vs2010的sdf文件和ipch文件夹

From: http://hi.baidu.com/vcdashi/blog/item/f25a3511afbf136bcb80c40e.html vs2010打开某个工程 &#xff0c;转换成功之后&#xff0c;在工程目录看见一个sdf文件和一个ipch文件夹&#xff0c;都超大&#xff08;起码几十M&#xff09;&#xff0c;我一个四百多k 的工程&a…

vue音视频播放器 vue-hls-player

vue音视频播放器 vue-hls-player

Taro+react开发(68):页面数据太长不走url走localstorage

componentDidShow() {let arr localStorage.getItem("answerList");console.log(JSON.parse(arr), "answerList");}

湖南(包括岳阳)的交通规划

网上找到的&#xff0c;关心家乡建设的可以看看。转载于:https://blog.51cto.com/fredlin/275734

beetl 页面标签_Beetl 2.9.0 发布,修改 HTML 标签的渲染机制

本次发布主要修改了 HTML 标签的渲染机制&#xff0c;HTMLTagSupportWrapper2 采用延迟渲染在2.9.0版本&#xff0c;HTML 标签内部渲染是使用tagBody变量&#xff0c;渲染的时候会调用此变量的toString方法完成按需渲染功能。理论上如果没有对beetl引擎的HTML 标签内部实现做任…

手把手教你用好LINQ to SQL(1)

From: http://developer.51cto.com/art/200904/120915.htm 作者写下这篇文章的主要目的不是“一口吃成个胖子”让大家一次就学会LINQ to SQL。而是从不同的方面帮助大家正确理解&#xff0c;所以这里提到的快速的含义是提高效率&#xff0c;而不是单纯为了学习的速度。 最近接连…

长期对着电脑是对身体很哟影响的

笔记本电脑由于其轻,灵活性和新设施像WiFi,允许人们使用互联网几乎无处不在,已经成为许多人的一个组成部分的生活尤其是年轻人。每个人都同意,有一台笔记本电脑是没有害处的 。但问题是,人们紧张他们的眼睛在非常不健康,长时间不合适的姿势。年轻人使用大部分的休闲时间在他们的…

Taro+react开发(69):传入localstoage需要jsonstringfy处理

this.setState({answerList: [...answerList, ...[val]],...val},() > {localStorage.setItem("answerList", JSON.stringify(answerList));});

一个关于人工智能实现的讨论

突然发现一场战争刚刚过去&#xff0c;自己没有来得及参战&#xff0c;战利品当然就没有机会了&#xff0c;不过观战也是不错的选择。这篇帖子讨论的非常不错&#xff0c;大家都阐述了自己的观点&#xff0c;值得一看 前面写过一些文章来讨论人工智能&#xff0c;我的观点就是以…

vue移动端的一款音频插件vue-aplayer

vue移动端的一款音频插件vue-aplayer

VC中为对话框添加背景图片

From: http://blog.ezcn8.com/2011/06/11/vc%E4%B8%AD%E4%B8%BA%E5%AF%B9%E8%AF%9D%E6%A1%86%E6%B7%BB%E5%8A%A0%E8%83%8C%E6%99%AF%E5%9B%BE%E7%89%87/ VC中为对话框添加背景图片也很简单&#xff0c;可以有以下两种方法&#xff1a; 1. 使用画刷CBrush void CDlg::OnPaint()…

再好的工作是为了更好的生活

2015-02-08 00:11 不过没有的好的工作是不容易好好生活的。 //z 2015-02-08 00:11:35 L.32685705 BG57IV3BYH T1976877610.K.F2506529148[T1,L106,R4,V35] 感动就像糖醋排骨里的糖&#xff0c;光有白糖虽然很甜&#xff0c;可是甜得没有根据&#xff0c;多吃几口就腻歪了&#…

隐藏自己电脑的IP地址

你的电脑总会受到别人***吗&#xff1f;隐藏自己电脑的IP地址&#xff0c;可以让别人难以***。这里就为大家介绍一种隐藏IP的方法&#xff1a; 第一步&#xff1a;用本地安全策略&#xff0c;先打开IP安全策略&#xff0c;方法是在“运行”里输入 secpol.msc或着在控制面板里打…

esxi管理端口_网工知识角|一分钟轻松了解华为端口安全机制

学网络&#xff0c;就在IE-LAB国内高端网络工程师培养基地为防止MAC地址洪泛攻击&#xff0c;防止未经允许的设备访问网络&#xff0c;并增强安全性。通过端口安全机制&#xff0c;录连接到交换机端口的以太网MAC地址&#xff0c;只允许某个或者某些合法设备通过相应的端口进行…

VC设置CEdit控件背景透明、文字背景也透明

开发环境&#xff1a;VC6&#xff0c; a dialog based MFC application. 主界面&#xff1a; 为对话框添加WM_CTLCOLOR消息响应函数&#xff1a; HBRUSH CDDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {HBRUSH hbr CDialog::OnCtlColor(pDC, pWnd, nCtlColor);…

[leetcode]Generate Parentheses

题目&#xff1a; Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. For example, given n 3, a solution set is: "((()))", "(()())", "(())()", "()(())", "()()(…

小议程序员编写技术文档

一提到写文档&#xff0c;可能很多程序员可能会不屑一顾&#xff0c;但是&#xff0c;无论处于规范开发流程&#xff0c;还是就于逃避嫌责的目的&#xff0c;能够将自己所从事的工作用文档描述记录下来&#xff0c;还是一件很有成就感的事情&#xff0c;抛开其功用不谈&#xf…

Taro+react开发(71):Taro.nextTick

Taro.nextTick(callback) 延迟一部分操作到下一个时间片再执行。&#xff08;类似于 setTimeout&#xff09; 说明 因为自定义组件中的 setData 和 triggerEvent 等接口本身是同步的操作&#xff0c;当这几个接口被连续调用时&#xff0c;都是在一个同步流程中执行完的&#xf…