2015我想和Java聊聊之集合大阅兵

来源:互联网 发布:买了域名要主机吗 编辑:程序博客网 时间:2024/04/28 10:45

java的容器和数组是java里很常用的一块。上一章写HashMap和Hashtable的区别,其实Hashtable是老旧的类别,除了特殊的对 线程安全有要求的场景,一般来说,是弃之不用的,同样情况的还有Vetor和stack等。一般来说,JAVA的可变数组下常用的为ArrayList、 LinkedList、HashSet、TreeSet、LinkedHashSet,映射表下常用的为HashMap、LinkedHashMap,TreeMap。现连同数组一起来介绍他们的区别和常用场景。


数组

《Thinking in java》第16章对数组有如下:数组是效率最高的存数和随机访问对方引用序列的方式,数组是限行序列,这使得数组元素访问非常迅速。不过数组在它的生命周期里长度是固定的,应用场景不如ArrayList丰富。数组对泛型的支持也不太好。比如:

Object[] os = new Object[]{3,"a","asdasd",5,6f};  Arrays.sort(os);  System.out.println(Arrays.toString(os));



会抛出异常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String



数组速度很快,可以本身除了object的方法外仅有length方法,实际使用中要与Arrays结合:
Arrays.sort 排序;
Arrays.toString 输出为字符串
Arrays.fill 全部填充为某个对象
Arrays.binarySearch 查找对象(适用于Arrays.sort 排序后)


List(列表)

ArrayList的长度是可变的。ArrayList底层是由数组支持,而LinkedList是由双向链表实现,每个对象包含数据和链表的前一个和后一个元素的引用,如果经常在list里删除或者插入元素,用linkedlist更合适,否则用arraylist更快;ArrayList在插入数据的时候,必须创建空间将它所有的引用向前移动,这样随着arraylist的增加而越来越慢,Linkedlist只需要链接新的元素,而不需要修改列表剩余的元素。
由于ArrayList底层是由数组支持,所以数组和List之间是可以通过Arrays.asList互相转换的

String[] ss = new String[]{"1","asda","asdasdasda","zxzzxczcx"}; List<String> list = Arrays.asList(ss);


不过需要注意,由于数组长度是固定的,所以转换后的List长度也是固定的,一旦调用修改集合尺寸的方法,就会抛出UnsupportedOperationException异常,所以需要将
 Arrays.asList的结果作为构造器的参数传给List:
List list2 = new ArrayList<String>(list);


和数组的Arrays类似的是,List具有一个Collections工具类,
Collections.replaceAll     替换所有符合条件的对象;
Collections.fill    全部替换;
Collections.sort    排序;
Collections.binarySearch    查询;


Set(集)

Set和List最直观的区别是Set是不可重复的(Set存放的是对象的引用,没有重复对象;List的特征是其元素以线性方式存储,集合中可以存放重复对象),所以在HashMap维护Key值需要用到HashSet
HashSet最快最常用,LinkedHashSet保持元素的插入顺序,TreeSet基于TreeMap,生成一个总是处于排序状态的set
由于List和Set 都extends了Collection,所以Collections的一部分方法也适用于Set,比如:
Collections.max(set);
Collections.min(set);


Map(映射表/关联数组)

HashMap是无序的,利用HashCode散列的数据结构,增快查询速度;LinkedHashMap键值对是顺序插入的,在迭代输出的时候更快,new LinkedHashMap<String, Object>(6,0.75f,true),采用基于访问的最近最少使用的算法(LRU),没有被访问的元素会被排在最前面,在维护散列元素的同时还要维护链表。
Map的性能瓶颈在于随机读取数据时候,对键的查询,由于键是使用简单的现行查询,速度很慢,而HashMap利用上散列(HashCode)使得查询速度加快。HashMap不继承Collection,所以不具备Iterator方法,但是它内部维护了Keyset和EntrySet,利用entrySet可以进行遍历:

Iterator<Entry<String,Object>> its = map.entrySet().iterator();


LinkedHashMap和LinkedHashSet以及LinkedList都维护了双向队列的方法,因此速度会相应减慢。


免锁集合

Vetor和Hashtable都是线程安全的集合,但是在单线程情况下性能较差而被淘汰,可以上述的集合并不能满足线程安全场景的需求,往往在多线程情况下需要对这些集合的操作加锁,这不免增加了代码的冗余和性能的下降,为了性能调优,JDK1.5引入了java.util.concurrent包,包里有很多免锁集合:CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap。以ConcurrentHashMap为例:
ConcurrentHashMap锁的方式是稍微细粒度的。 ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。
试想,原来 只能一个线程进入,现在却能同时16个写线程进入(写线程才需要锁定,而读线程几乎不受限制,之后会提到),并发性的提升是显而易见的。 更令人惊讶的是ConcurrentHashMap的读取并发,因为在读取的大多数时候都没有用到锁定,所以读取操作几乎是完全的并发操作,而写操作锁定的粒度又非常细,比起之前又更加快速(这一点在桶更多时表现得更明显些)。只有在求size等操作时才需要锁定整个表。
根据测试,ConcurrentHashMap的性能比同步的HashMap快一倍左右。免锁集合在后续的并发和线程安全这块会详细介绍。




0 0
原创粉丝点击