Java的集合(三)
8、数组和链表简介
在计算机中要对给定的数据集进行若干处理,首要任务是把数据集中的一部分(当数据非常大时,可能只能一部分一部分地读取数据到内存中来处理)或全部存储到内存中,然后在对内存中的数据进行各种处理。
例如:对如数据集S{1,2,3,4,5,6},要求S中元素的和,首先要把数据存储到内存中,然后再将内存中的数据相加。
当内存空间中有足够大的连续空间时,可以把数据连续的存放在内存中,各种编程语言的数组一般都是按这种方式存储的(也可能有例外),如图 1(b);当内存中只有一些离散的可用空间时,想连续存储数据就非常困难了,这时能想到的一种解决方式是移动内存中的数据,把离散的空间聚集成连续的一块大空间,如图 1(c)所示,这样做当然也可以,但是这种情况因为可能要移动别人的数据,所以会存在一些困难,移动的过程中也有可能会把一些别人的重要数据给丢失。另外一种,不影响别人的数据存储方式是把数据集中的数据分开离散地存储到这些不连续空间中,如图(d)。这时为了能把数据集中的所有数据联系起来,需要在前一块数据的存储空间中记录下一块数据的地址,这样只要知道第一块内存空间的地址就能环环相扣地把数据集整体联系在一起了。C/C++中用指针实现的链表就是这种存储形式。
由上可知,内存中的存储形式可以分别分为连续存储和离散存储两种。因此,数据的物理存储结构就有连续存储和离散存储两种,它们对应了我们通常所说的数组和链表。
9、数组和链表的区别
数组是将元素在内存中连续存储的;它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;它的缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据量比较大的时候,有可能会出现越界的情况,数据量比较小的时候,又可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低。
链表是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)。
10、链表和数组使用场景
数组应用场景:数据比较少;经常做的运算是按序号访问数据元素;数组更容易实现,任何高级语言都支持;构建的线性表较稳定。
链表应用场景:对线性表的长度或者规模难以估计;频繁插入删除操作;构建动态性比较强的线性表。
参考博客:http://blog.csdn.net/u011277123/article/details/53908387
11、Java中ArrayList和LinkedList区别?
ArrayList和Vector使用了数组的实现,可以问为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向(??)。
LinkedList使用了循环双向链表数据结构。LinkedList链表是由一系列表项连接而成。一个表项总是包含三个部分:元素内容、前驱表和后驱表,如图所示:
在下图展示了一个包含三个元素的LinkedList的各个表项间的连接关系。在JDK的实现中, 无论LinkedList是否为空,链表内部都有一个header表项,它既表示链表的开始,也表示链表的结尾。表项header的后驱表便是链表中的第一个元素,表项header的前驱表便是链表中最后一个元素。
12、List a = new ArrayList()和ArrayList a = new ArrayList()的区别?
List list = new ArrayList();这句创建了一个ArrayList的对象后上溯到了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。而ArrayList list = new ArrayList();创建一对象则保留了ArrayList的所有属性。所以需要用到ArrayList独有的方法的时候不能用前者。实例代码如下:
List list = new ArrayList();
ArrayList arrayList = new ArrayList();
list.trimToSize();//错误,没有该方法
arrayList.trimToSize();
13、要对集合更新操作时,ArrayList和LinkedList哪个更合适?
1):ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2):如果集合数据是对于集合随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。
3):如果集合数据是对于集合新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和LinkedList是两个集合类,用于存储一系列的对象引用(references)。
一、时间复杂度
首先一点关键的是,ArrayList的内部实现是基于数组的,因此,它使用get方法访问列表中的任意一个元素时(random access),它的速度要比LinkedList快。LinkedList中的get方法是按照顺序从列表的一端开始检查,直到另外一端。对LinkedList而言,访问列表中的某个特定元素没有更快的方法了。
假设我们有一个很大的列表,它里面的元素已经排好序了,这个列表可能是 ArrayList 类型的也可能是 LinkedList类型的,现在我们对这个列表来进行二分查找(binary search),比较列表是 ArrayList 和 LinkedList 时的查询速度,看下面的程序:
这个结果不是固定的,但是基本上ArrayList的时间明显要小于LinkedList的时间。因此在这种情况下不宜用LinkedList。二分查找法使用的而随意访问(rondom access)策略,而LinkedList是不支持快读的随机访问的。对一个LinkedList做随机访问所消耗的时间与这个list的大小是成比例的。而对应的,在ArrayList中进行随机访问所消耗的时间是固定的。
二、空间复杂度
每个 Entry 对象 reference 列表 中的一个元素,同时还有在 LinkedList 中它的上一个元素和下一个元素。一个有 1000 个元素的 LinkedList 对象将有 1000 个链接在一起 的 Entry 对象,每个对象都对应于列表中的一个元素。这样的话,在一个LinkedList 结构中将有一个很大的空间开销,因为它要存储这 1000 个 Entity 对象的相关信息。ArrayList 使用一个内置的数组来存 储元素,这个数组的起始容量是 10.当数组需要增长时,新的容量按如下公式获得:新容量=(旧容量*3)/2+1,也就是说每一次容量大概会增50%。 这就意味着,如果你有一个包含大量元素的 ArrayList 对象(基数太大),那么最终将有很大的空间会被浪费掉,这个浪费是由 ArrayList 的工作方式本身造成 的。如果没有足够的空间来存放新的元素,数组将不得不被重新进行分配以便能够增加新的元素。对数组进行重新分配,将会导致性能急剧下降。如果我们知道一个 ArrayList 将会有多少个元素,我们可以通过构造方法来指定容量。我们还可以通过 trimToSize 方法在 ArrayList 分配完毕之后去掉浪 费掉的空间。
※ 三、总结
ArrayList和LinkedList在性能上各有优缺点
1):对 ArrayList 和 LinkedList 而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList 而言,主要是在内部数组中增加一项,指向所添加的元素,偶 尔可能会导致对数组重新进行分配;而对 LinkedList 而言,这个开销是统一的,分配一个内部 Entry 对象。
2):在 ArrayList 的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在 LinkedList 的中间插入或删除一个元素的开销是固定的。
3):LinkedList 不支持高效的随机元素访问。
4):ArrayList 的空间浪费主要体现在在 list 列表的结尾预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList 会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用 LinkedList 了。