dryr

开发-技能-java集合框架

    java集合框架(collections framework)是一个著名的java框架,框架负责人Joshua Bloch是sun公司的软件架构师,也是effective java(02年Jolt大奖)的作者,典型的软件设计牛人。据说java集合框架被誉为是JDK中设计的最好的一个部分。下面是根据jdk1.6.0的api文档整理的java集合框架的UML图(接口和类实在太多了,只整理了接口,对整个框架来讲:一般接口还会有一个抽象骨架类,然后会根据数据结构或用途的不同再有几个接口实现):

开发-技能-java集合框架 - dryr - 要有追求,但不强求

     随着JDK版本的升级,发现java集合框架也增加或更新了很多的内容,每个版本的类图可能都会有点不一样,但万变不离其宗,总体的设计思路和接口继承关系基本都不会动。另外说明一下,java.util.Collections和java.util.Arrays是两个工具类,里面包含一些集合相关的比较有用方法,比如从数组产生list视图,对collection的排序sort。
集合框架中包含List,Queue,Set和Map这四大块,注意:Map虽然属于集合框架,但Map接口并不从Collection接口扩展。

一、List
List是Collection的一个重要组成部分,在集合框架中,JDK提供的List相关类的UML图如下:

开发-技能-java集合框架 - dryr - 要有追求,但不强求

List中JDK提供的实现类有4个,ArrayList,LinkedList,Vector和Stack。这4个类中,Vector和Stack是早期的集合相关类,这两个类都实现了同步,并且在设计时也存在一定的缺陷,所以现在一般不推荐使用这两个类,一般使用ArrayList来替代Vector,ArrayDeque来替代Stack。
ArrayList的内部通过数组来实现,LinkedList的内部采用链表来实现,这两者都支持随机访问,但两者也有各自适合使用的场景:一般来说,如果存在需要在列表中间插入或删除元素,推荐使用LinkedList,因为LinkedList采用链表实现,中间插入元素只需要对两个相邻的元素的相邻元素引用进行修改即可;而ArrayList如果在中间插入,因为数据结构采用数组,所以插入后的元素都需要往后退后一位,更有甚时会导致数组的重新分配,所以性能成本非常高。而顺序插入或删除元素的场景则推荐使用ArrayList,这样可以发挥数组的快速随机访问的特性。
ArrayList中的数组是需要根据ArrayList中元素的数量而变化的,使用new ArrayList()新建时会默认先创建一个长度为10的Object数组,之后在性新插入元素的元素大于数组长度时,会新建一个长度=(原数组长度*3)/2+1的一个新数组,并将原数组中的元素拷贝到新数组,所以在ArrayList中的元素超过内部数组长度时的成本是蛮高的,所以如果可以预见在ArrayList中需要增加比较多的元素,则可以在使用new ArrayList(n)来新建一个具有较大容量数组的一个列表,或者也可以通过ensureCapacity(n)来直接将ArrayList的内部数组的容量加大到足够的地步,这可以避免频繁的重建内部数组,当然如果n取得过大会导致空间浪费。
通过iterator方法可以获取List的迭代器,在abstractList的迭代器的实现中,iterator.remove方法是删除最后返回的列表元素,所以如果还没有使用iterator.next()就直接调用remove方法的话为抛出IllegalStateException异常,连续多次调用remove方法也会抛出同样的异常。另外针对ConcurrentModificationException异常,是通过判断迭代器保持的修改数expectedModCount与list实际修改数modCount的比较来判断的,不相等则抛异常,remove时会重置迭代器内保存的修改数等于list实际修改数所以也不会抛出此异常。调用了迭代器的remove方法后迭代器内的游标会执行“--”操作。
List接口扩展了ListIterator接口(具体实现在abstractList中),可以进行前后双向遍历,还可以进行插入删除操作。调用了迭代器的remove方法后迭代器内的游标会执行“--”操作;调用了迭代器的add方法后迭代器内的游标会执行“++”操作。这就是下面代码只打印了"111""222""333"而没有打印“444”的原因所在:
list.add("111");
list.add("222");
list.add("333");
for(ListIterator listIte = list.listIterator();listIte.hasNext();){
        System.out.println(listIte.next().toString());
        listIte.add("444");
        System.out.println(list.size());
}
Vector和Stack是实现了同步的,而ArrayList和LinkedList的实现不是同步的。这是因为这两个类在大部分情况下并不需要使用在并发场景中,所以没有必要实现同步反而降低了性能。当然如果确实需要使用在并发场景中的话,jdk也提供了Collections.synchronizedList方法将这两个类包装成实现同步的List(这最好在创建列表时完成),代码示例:List list = Collections.synchronizedList(new ArrayList()); 或List list = Collections.synchronizedList(new ArrayList(),mutex);
这个同步是通过对每个接口方法都包装了一层同步,如果没有指定同步对象,则对自身实例进行同步。另外需要说明:通过此得到的同步的list对象,在进行迭代器(iterator和listIterator)在多线程场景使用时还是需要由用户进行手工同步。

二、Map

三、Queue

四、Set
Set是Collection的一个重要组成部分,在集合框架中,JDK提供的Set相关类的UML图如下:

开发-技能-java集合框架 - dryr - 要有追求,但不强求

 
其他说明:
关于迭代器的快速失败机制:指当集合类在迭代器外进行了修改后,迭代器将会在访问时立即抛出ConcurrentModificationException,而不会当访问到被修改过的元素时才抛出异常。举例:修改了第5个元素,但当前迭代器去访问第2个元素,则迭代器也会抛出异常,而不是访问到第5个元素时才抛出异常。

评论