JAVA collections 其实是很值得深入研究的一块,说多不多,说少不少。Collections的大家族里像List , Map 是最为常见且最为实用的两个核心对象,无论哪一块代码似乎都离不开这两个兄弟,可是作为JAVA Collection,你真的了解么? 这篇文章通过一系列的有价值的分析得出一个使用Collections的最佳实路,Here we go!
List
在分析之前我们需要定好分析的需求,一个好的需求才能得出最好的分析结果,那么我们到底想要什么呢?
1.线程安全
2.SIZE
3.常用操作方法(adding, removing, accessing or iterating)
以上三点应该就是我们分析的切合点,我们所写的Testcase应该围绕着这三点来写
在此,应该还是强调的说明一下,如果你想要线程安全的话,请使用Vector或才Stack,因为他们都是同步方法,而ArrayList和LinkedList则是非线程安全的,请注意这点。
Add
数据规模 | 操作类型 | ArrayList | Vector | LinkedList |
50000 objects | Add object at end | 10 | 20 | 50 |
…at middle | 2553 | 2574 | 96553 | |
…at first | 5848 | 6078 | 30 |
上面的数字代表操作所用的时间,其实不难看出一个结论:
1. 如果要在尾部插入数据,ArrayList是最快的
2.如果在在头部插入数据,LinkedList是最快的
3.Vector的任何操作都比ArrayList要慢一点,因为他是同步方法,有一定的效率成本。
4.LinkedList只在进行头尾操作时,效率比较高。
以上这些就是简单的通过TestCase来获取的一个结论,当然如果你还记得起数组,链表之间的数据结构的话,这应该是很简单的吧。
Remove
数据规模 | 操作类型 | ArrayList | Vector | LinkedList |
50000 objects | Remove object at end | 60 | 62 | 15 |
…at middle | 1810 | 1815 | 46850 | |
…at first | 6155 | 6156 | 0 |
这个结论应该一目了然,不再进行详述了。
Access
数据规模 | 操作类型 | ArrayList | Vector | LinkedList |
100000 objects | Access object at end | 0 | 50 | 211850 |
…at middle | 0 | 30 | 457090 | |
…at first | 0 | 0 | 15 |
通过Access的评测,你可以发现,ArrayList和Vector几乎没有读取成本,因为他们遍历数据使用的是索引,而LinkList就悲剧了,他之所有有如此高的开销,是因为他要遍历所有Node进行仿问Objects。
关于Iterating collection:
单纯的Iterating没有什么可说的,效率都差不多,因为都是一个接一个的遍历,和数据结构就无关了。不过推荐使用ListIterating,因为你可以从任何一边进行遍历而仅仅只有一点点可以忽略不计的忽不足道的开销(10000的对象的ListIterator仅仅比Iterating多开销3ms.)
Set
Set是一个唯一集合,他有两个实现类,一个是HashSet,一个是TreeSet。
顾名思义,HashSet的效率会比TreeSet高很多,因为Treeset是有序的集合。 这里有一个很重要的最佳实路,如果你要使用有序的TreeSet,请在数据录入阶段使用HashSet进行数据插入,最后将HashSet转化为TreeSet。你可能会有所疑问,为什么要这样呢? 因为TreeSet在每一次数据插入时,它都会进行一个集合排序,而这样是一个很大的性能开销,我们往往只需要一个最终的有序的集合,而不是一个过程中的有序集合,所以往往在项目中你看到直接在TreeSet上进行大量的数据更改时,请务必将其改为HashSet。
Map
Map是以Key-value的形式存在的集合。他的实现子类有:Hashmap , HashTable, WeakHashMap , TreeMap
同样的,HashMap,WeakHashMap是非线程安全的,而且HahsTable是线程安全的。
HashMap没有太多的可以补充的,不过有一点要注意,就是HashMap的碰撞会导致很多可能非常严重的问题,因为HashMap的唯一性是通过KEY的HASH值的唯一性来确定的,而往往一个Hash值却有着多个因子,这就造成了实际上不同的KEY却有可能生成同一个HASHCODE而被JAVA误认为是唯一解,那这个时候,JAVA的BUG就产生了,他会将KEY-VALUE转换成链表状态,你的值不会丢失,但是这个效率将异常低效。更多详细的解释在我之前的一篇文章上有所提及,如果有兴趣的话,可以移步去那里看看: 浅析 HashTable 碰撞拒绝服务漏洞
———————————————————————————————————————
通过上面的简单分析,我们可以得出以下的比较实用的最佳实践:
Lists:
1. 如果你频繁的在中间或末尾操作数据,请使用Arraylist(如需要线程安全,请使用Vector)
2.如果你频繁的在头部操作,请使用LinkedList
3.最好使用ListIterator
Sets:
1.用HashSet来保存唯一数据对象
2.用TreeSet来存储有序的数据集合
Maps:
1.最好使用HahsMap
2.用TreeMap来保存有序集合
(请注意Hash碰撞)