如何选择集合

在编程的过程中,选择何种集合至关重要,下面由我来总结下选择集合的方法

 

选择集合所考虑的关键问题在于:效率代价与空间代价的平衡问题。

效率代价是指执行的效率,简单的说如果一个资源没有把索引记录下来,那么要找到他你就需要执行程序,那么你的代价在于系统花钱了时间。

空间代价是指存放的空间消耗内存的代价,如上边说到的如果把索引记录下来很方便就能找到要找的资源,也就是用空间代价换取运行时间的缩短。这就像电话本除了记号码还要记人名,总不能一个个打电话去问谁是张三李四的吧。

我想说的是,现在的储存器越来越便宜,空间可以说不是难题,空间代价不是优先考虑的问题。

 

下面我们来分析下各种常用的集合数据类型

1.       ArrayList:数组实现的保持进入顺序的集合。
查询元素操作(提供索引位置):计算元素内存指针位置,一步便可跳到。
增加元素操作:查询到增加位置的元素,后面的元素挨个向后移一个位置,保存元素的      时间可以忽略不计
删除元素操作:查询到增加位置的元素,后面的元素挨个向前移一个位置,清空元素的时间可以忽略不计
修改元素操作:与查询元素操作效率相同

2.       LinkedList:链表实现的保持进如顺序的集合。
查询元素操作(提供索引):从根节点遍历,不能跳越,直到索引要求的位置。
增加元素操作:查询到增加位置的元素,接上链的时间忽略不计
删除元素操作:查询到增加位置的元素,拆下链的时间忽略不计
修改元素操作:与查询元素操作效率相同

3.       HashSet/HashMap:无序的Hash算法来索引的集合
查询元素操作:HashCode经过Hash算法直接指向内存地址,而后调用指向的数据的equals(),如果不等,继续Hash算法。
增加元素操作:查询到增加位置的元素,保存的时间忽略不计
删除元素操作:查询到增加位置的元素,删除的时间忽略不计
修改元素操作:与查询元素操作效率相同

4.       TreeSet/TreeMap:根据指定值排序的集合,低层是链表
查询元素操作:红黑二分查找树算法,链表遍历跳过部分节点,效率大于LinkedList
增加元素操作:查询到增加位置的元素,接上链的时间忽略不计

删除元素操作:查询到增加位置的元素,拆下链的时间忽略不计
修改元素操作:与查询元素操作效率相同

 

性能分析表:

集合/操作

A

B

C

D

顺序性

ArrayList

C+

=A

   

=C

进入顺序

LinkedList

C+

=A

=C

进入顺序

HashSet/HashMap

C+

=A

   

=C

无顺序

TreeSet/TreeMap

C+

=A

   

=C

指定顺序

注释:=A表示效率上等于其A操作,也就是增操作,=C也同理;C+①表示查询效率要加入计算。

 

从上面中最核心的问题是ArrayList增删操作与LinkedList查找操作代价比较

案例1ArrayListLinkedListadd/remove操作的比较

分析1:低效率ArrayList.add/remaove操作的原因在于增删操作可能会引起元素的交换,而LinkedList的增删只需要接链头和链尾。

结果数据1ArrayList100万次的交换消耗了3mm

 

案例2ArrayListLinkedListget操作的比较

分析2LinkedList的查找操作效率低的原因是必须遍历过程中的每个节点,但是遍历一次仅相当于getter方法调用消耗非常小。

结果数据2LinkedList166万次的遍历都消耗都不到1mm

 

结论:通过以上案例测试得知,遍历元素与交换元素的效率差至少是十万级的。所以我们几乎可以忽略LinkedList查询的效率,而HashSet/HashMap,TreeSet/TreeMap效率比LinkedList还要高,也可以忽略其查询效率。

由此,应修改性能分析表如下:

 

性能分析表:

集合/操作

A

B

C

D

顺序性

ArrayList

=A

=C

进入顺序

LinkedList

=A

=C

进入顺序

HashSet/HashMap

=A

=C

无顺序

TreeSet/TreeMap

=A

=C

指定顺序

 

总结:

1:如果元素没有顺序要求,有优的选择是HashSet/HashMap,其增删查找的效率都很高。

2:如果元素有顺序要求,对于ArrayListLinkedListTreeSet/TreeMap都可以实现,只是ArrayListLinkedList需要控制进入顺序,而TreeSet/TreeMap需要值记录顺序。如果记录顺序的值很方便提供,优先选择TreeSet/TreeMap

3:如上述有顺序要求,而记录顺序的值不方便提供的情况,如Stack,如果提供入栈时间比较冗余。这个时候则考虑集合会不会出现元素顺序改动的增删操作,如果会则选择LinkedList。如案例1,2中的结论ArrayList的单位次交换耗时至少是LinkedList遍历一个节点的50万倍以上。

4:如上述情况有顺序要求,并且不希望提供值来排序,而且集合的顺序基本不会发生改变,选择ArrayList

 

选择Set还是Map?

误点1:Map比Set数据多出一个维度,那么就可以利用多出的部分影响顺序或多出快捷收索功能。

正解1:原则是一个集合对象只能提供一个索引。

    对于Map其有效的索引则是key(实际上是key的equals和HashCode),而value是不能主导一个条目的,如Map的remove就不能通过value来删除,Map也不支持Value找到key的方法(如果iterator遍历找到key那就不能称之为索引到)。

    还是一个真理一个集合仅提供一个索引,Set和Map的区别只在于,索引是否唯一且数据本身就可以提供。只有满足这两个条件才可以使用Set。因为Set有赖于元素本身的equals/hashCode和comparTo方法,而这些方法只能由属性运算得出结果,所以属性要求有能力提供出索引。而Set的元素类型一旦被确定,也就说用两个Set为同一个数据加索引,其索引规则相同,这是因为元素类独有一份equals/hashCode和comparTo方法的实现。

    如果索引需要多个或数据对象的属性没有能力给出此索引,那么我们会选择Map。Map的key可以是我们定制的对象类型,也就是提供不同的equals/hashCode和comparTo的方法实现,而不像Set只能调用到数据本身的一种实现。选择不同的key类型就会得到不同的方法实现,建立不同的索引,从而多个Map能实现多个索引。

    如果希望同一个数据可能会被多种值检索到,那么就为他们各自建立一个Map,多个Map多个索引方式。Map总是能实现索引,而Set只能提供自身的一种索引,Set是Map的特例情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值