Collection接口(继承Iterable接口)
├List接口 (允许空值,允许有相同的元素,迭代器快速失败)
│├LinkedList类 (非同步)
│├ArrayList类 (非同步)
│└Vector类(同步)
│ └Stack类
└Set接口(最多允许一个空值,不允许有相同的元素,迭代器快速失败)
└TreeSet类(非同步)
Map接口
├Hashtable (同步,key和value不能为null)
├HashMap (非同步,key和value可以为null)
└WeakHashMap (非同步,key和value可以为null)
Collection接口
Collection是最基本的集合接口,JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List)实现。
所有实现Collection接口的类(通常通过它的一个子接口间接实现 Collection)都必须提供两个标准的构造函数:一个是无参数构造方法,用于创建空 collection;另一个是带有Collection 类型单参数的构造方法,用于创建一个具有与其参数相同元素的新collection。实际上,后者允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。尽管无法强制执行此约定(因为接口不能包含构造方法),但是 Java 平台库中所有通用的Collection 实现都遵从它。
如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个常用接口是List和Set。
迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的remove 或add 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
List接口
List是有序的Collection,用户可以根据元素的整数索引(在列表中的位置,从0开始)访问元素,并搜索列表中的元素,即add,remove,get都可以指定index。
List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个ListIterator()方法,该方法除了允许 Iterator 接口提供的正常操作外,还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList类
允许null元素。List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列 (deque)。
LinkedList不是同步的。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList实现了可变大小的数组。允许null值。ArrayList不是同步的。
List list = Collections.synchronizedList(new ArrayList(...));
Vector类
Vector非常类似ArrayList,但是Vector是同步的。
Stack 类
Stack继承自Vector。
Set接口
Set不能包含重复的元素,最多有一个null元素。
请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。
Map接口
请注意,Map没有继承Collection接口,Map提供key到value的映射。Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。它的key和value不能为null,否则运行时会抛出NullPointerException。
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
Hashtable是同步的。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null value和null key。但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
总结
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
ArrayList和LinkedList,该用哪个
ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以它更象一个链表结构,所以它们在性能上有很大的差别。
(1) 从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列 数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能。
(2)而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
(3)如果在编程中1,2两种情形交替出现,这时你可以考虑使用List这样的通用接口,而不用关心具体的实现,在具体的情形下,它的性能由具体的实现来保证。
设置集合类的初始大小
在Java 集合框架中的大部分类的大小是可以随着元素个数的增加而相应的增加的,我们似乎不用关心它的初始大小,但如果我们考虑类的性能问题时,就一定要考虑尽可能地设置好集合对象的初始大小,这将大大提高代码的性能,比如Hashtable载入因子为0.75,所以如果你知道在创建一个新的Hashtable对象时就知道元素的确切数目如为110,那么,就应将其初始大小设为110/0.75=148,这样就可以避免重新组织内存并增加大小。
有待验证的:
1、当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。