1 大类别的选择
- 当想要装非重复元素时,选择set
- 当想要保证装入的顺序时,选择List
- 当要以Key-value的形式存储时,选择Map
- 当想要保证元素先进先出时,选择Queue
2 set集合之间的选择
- HashSet:是不保证元素的排列顺序的,并且是线程不安全的,且集合元素可以为null。
- LinkedHashSet:将会按元素添加的顺序访问元素。由于LinkedHashSet需要维护元素的插入顺序,因此性能差于HashSet但在迭代访问元素时,有很好的性能。
- TreeSet:利用红黑数来存储元素,保证集合中的元素处于有序状态(排序规则有自然排序-comparable和定制排序comparator)。所以在需要进行元素排序(不是按它的插入顺序哈!!!)时,选择它。
- EnumSet:转为枚举类设计的集合类,EnumSet中元素必须是指定枚举类的值。
性能比较,一般情况下:
EmumSet好与HashSet好于LinkedHashSet好于TreeSet
需要注意的是,HashSet,LinkedHashSet,TreeSet,EmumSet都是线程不安全的,且添加元素可以为null。需要手动保证Set集合的同步性。通常可以使用Collection的synchronizeSet方法还”包装“Set集合,此操作最好在创建Set时进行。例子:
public class Main {
public static void main(String[] args) {
//set是线程不安全的,插入元素可以为空
Set<Integer> s = new LinkedHashSet<Integer>();
s.add(null);
HashSet<Integer> hs = new HashSet<Integer>();
hs.add(null);
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(null);
EnumSet e = EnumSet.allOf(teacher.class);
e.add(null);
}
}
enum teacher {
A, B, C, D
}
3 List之间的选择
ArrayList和Vector都是List接口的两种实现,都是通过动态的运行在分配的Object数组实现的。ArrayList和Vector提供了重新分配Object[]数组的方法。
Vector有很多缺点,通常推荐使用ArrayList。ArrayList是线程不安全的,Vector是线程安全的,因此ArrayList的性能要比Vector的性能好。
其实Arrays工具中提供了一个==asList(Object……a)==方法将数组或多个参数转换为一个List集合。这个List不是ArrayList和Vector的实例,而是Arrays中的内部类Arrays.ArrayList的实例,他是固定长度的,只能访问元素,而不能改变元素的个数。例子:
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> l= Arrays.asList(new Integer(3),new Integer(2));
System.out.println(l);
l.add(new Integer(1));//会报这个异常,Exception in thread "main" java.lang.UnsupportedOperationException
System.out.println(l);
}
LinkedList,若有频繁插入删除元素,选择LinkedList;若要随机访问元素,选择使用ArrayList。但是,总体来说ArrayList的性能要由于LinkedList的。
对于遍历,对ArrayList,Vector进行遍历时,采用随机访问方法get()遍历元素,性能更好。对LinkedList集合,采用迭代器Iterator进行遍历性能更好。
4 Queue之间的选择
PriorityQueue是Queue接口的一个实现类。它保存队列的顺序是按队列元素之间的大小顺序进行的,而不是按加入队列的顺序保存的。Priority不允许插入null值。当然它也有自然排序和订制排序两种排序方式。
Deque接口是Queue的子接口,它有一个实现类ArrayDeque,它即可以当做队列使用,也可以当做栈来使用。它的底层实现机制与ArrayList相似,也是采用一个可以重新调整大小的Object数组来保存元素的。注意,要使用栈这种数据结构时,推荐使用ArrayDeque和后面讲到的LinkedList,不要使用Stack!!!.
LinkedList类是List接口的实现类,也是Deque接口的实现类。它也可以作为双端队列来使用,因此也可以当做栈、队列来使用。LinkedList的实现机制是以链表的形式来保存元素的。
5 Map之间的选择
Map中key和value可以是任意引用类型的数据,其中key不允许重复。Map中有一个==keySet()==方法,用于返回key组成的set集合。其实我们对待Map可以像对待Set一样,因为Map是提供一个Entry的内部类来封装key-value的。Map也重写了toString()方法。
与HashSet相似,在HashMap,HashSet使用可变对象作为key时,当程序修改了作为key的可变对象后,程序再也无法访问到Map中被修改过的key了。因此尽量不要使用可变对象作为HashMap,HashTable,HashSet的key。
WeakHashMap的key保留的是实际对象的弱引用(HashMap的key保留的是实际对象的强引用)。这就意味着如果WeakHashMap对象的key所引用的对象没有被其它强引用变量引用时,则这些Key所引用的对象可能被垃圾回收机制回收,WeakHashMap也会删除相应的key-value。当然,既然使用了WeakHashMap的key保存对象弱引用,就不要让该对象有任何强引用,否则使用WeakHashMap就没意义了。
IdentityHashMap与HashMap的区别在于,IdentityHashMap当且仅当key严格相等(key1==key2)才认为两个key相等。而HashMap是认为key1和key2只要equals(),hashCode()相等,则认为两个key相等。例子:
public static void main(String[] args) {
IdentityHashMap ihm = new IdentityHashMap();
ihm.put(new String("wobushixiaobailian"), 99);
ihm.put(new String("wobushixiaobailian"), 89);
ihm.put("java", 99);
ihm.put("java", 19);
System.out.println(ihm);
}
输出结果:
各个Map的性能:EnumMap优于HashMap优于LinkedHashMap优于TreeMap。IdentityHashMap性能没有特别出色之处。
说到HashSet和HashMap的性能选项,就想到了负载因子(当前hash表的记录数除以当前hash表的容量:size/capacity)。hash表还有一个负载极限,它是一个0~1的数,当负载因子达到负载极限时,hash表会成倍的增加hash表的容量(桶的数量),这个过程叫做rehashing。负载极限的默认值为0.75。如果一开始就知道HashMap或HashSet会存大量的记录,可以初始化一个很大的容量,这样可以增加添加记录的效率,但是初始容量设置太高可能会浪费空间。