在项目中头一次见了师兄写了LinkedList来存对象,之前一直都用ArrayList,于是上网百度了一下,在这里写下自己的想法。
二者在继承上的的关系:(注:Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)==false,Set最多有一个null元素,而List允许有相同的元素。)
先说二者的数据结构上的特点:
ArrayList 的存储是基于对象数组的,即它一种动态数组的数据结构,言外之意是其在内存中的存储区域是连续的,相邻元素有其固定且相邻的地址;
LinkedList的存储是基于双向链表的。维基百科上说:链表是一种线性表,但是并不是按线性的顺序存储数据(存储地址不是相邻的,而是无规律的),而是在每一个节点里存到下一个节点的指针(Pointer)。链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。双向链表明显就是不仅有指向后一个节点的指针,还有指向前一个节点的指针。这样可以从任何一个节点访问前一个节点,当然也可以访问后一个节点,以至整个链表。这就决定LinkedList中访问节点的方式:通过相对位置来寻找。
基于二者在结构上的差别,使其具有不同的性能,体现在时间复杂度和空间复杂度上。
从空间复杂度来讲,对于ArrayList,如果我们事先不知道其元素的数目,那么在数组容量需要增加时,增加容量的计算公式是:新容量=(旧容量*3)/2+1;大概增加了50%的内存消耗,这显然是一种浪费;对于LinkedList,每个增加的内存都是零星的。在LinkedList类中有一个私有的内部类,定义如下:
private static class Entry {
Object element;
Entry next;
Entry previous;
}
每个 Entry 对象都对应着列表中的一个元素,同时还有在 LinkedList 中它的上一个元素和下一个元素。一个有 1000 个元素的LinkedList 对象将有 1000 个链接在一起的 Entry 对象。这样的话,在一个 LinkedList 结构中将有一个很大的空间开销,因为它要存储这 1000 个Entity 对象的相关信息。
从时间复杂度来讲,
1)随机访问速度 因为ArrayList的基于数组的特性,其随机访问便很快捷,可以通过下标进行一步地址相加操作来锁定内存位置,而LinkedList由于是基于相对位置,两个在index上相邻的元素的存储位置关系是任意的,它的.get(index)操作是从整个List的一端(较近一端)开始,要经历该端与index的间距数这样多的步数的遍历寻找来实现,故其随机访问是很耗时的。
2)增删元素速度 鉴于二者在存储方式上的差异,对ArrayList增加(或删除)一个元素时,需要其index之后的所有元素的复制和移动,这是一个耗时与原数组尺寸相关的工程,当原ArrayList很大时,就很耗时;对LinkedList增加(或删除)一个元素时,这个元素的地址是任意的,我们只需要对新增元素设置一个向前和一个向后的指针(分别指向LinkedList中的前一个和后一个元素),再对其前的元素和其后的元素分别增加一个向后和和向前的指针(指向新增元素)标记,更新工作就完成了,不涉及到其它元素的任何操作(删除时一样),其耗时是固定的。故在增删元素方面,LinkedList的性能优于ArrayList。
总结:
1.对 ArrayList 和 LinkedList 而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList 而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对 LinkedList 而言,这个开销是统一的,分配一个内部 Entry 对象。
2.在 ArrayList 的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动,开销与剩余元素量成正比;而在 LinkedList 的中间插入或删除一个元素的开销是固定的。
3.LinkedList 不支持高效的随机元素访问。
4.ArrayList 的空间浪费主要体现在在 list列表的结尾预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗相当的空间。
可以这样说:当操作是在一列数据的后面添加或删除数据而不是在前面或中间,并且需要随机访问其中的元素时,使用 ArrayList 会比较好;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用 LinkedList 了。