十、选择接口的不同实现
容器之间的区别通常归结于由什么数据结构实现的。
比如:ArrayList和LinkedList都实现List接口,所以基本操作都是一样的。然而ArrayList底层是数组实现的,而LinkedList是双向链表实现的,其中每个对象包含数据的同时还包含直想链表中前一个和后一个元素的引用。因此更适合用于插入、删除多的操作。而随机访问就应该选择ArrayLIst,根据不同操作的性能选择实现。
再比如:TreeSet、HashSet、LinkedHashSet都实现Set接口。每种都有不同行为:HashSet查询速度最快;LinkedHashSet保持元素插入的次序;TreeSet基于TreeMap,生成一个处于排序状态的Set。所以根据不同行为选择的实现。
对List的选择
对于数组支撑的List和ArrayList,无论列表的大小如何,访问速度都是一样的快。而对于LinkedList,访问时间对于较大的列表将明显增加。所以操作随机访问类型的操作,数组结构要比链表结构更合适。
当使用迭代器插入新元素时,对于ArrayList当列表变大时,开销变大。但对于LinkedList,并不会随着列表尺寸变化而明显变化。因为,ArrayList插入时,必须为数组扩展空间,并将引用向前移动。而LinkedList则只需要链接新的元素,而不必修改列表中剩余的元素。
LinkedList对List的端点会进行特殊处理——这使得LinkedList在作用于Queue时,效率提高。LInkedList中的插入和移除代价相当低廉,并且不会随着列表尺寸发生变化,但是对于ArrayList插入操作的代价高昂,并且代价将随列表尺寸增加而增加。
对于随机访问get() 和 set() 操作,ArrayList明显速度快于LinkedList,因为LInkedLIst不是针对随机访问设计的。
最佳的做法时选择ArrayList,只有经常插入和删除而影响性能时才会选择LinkedList.
对Set的选择
HashSet的性能总比TreeSet好,特别是在添加和删除元素时,而这两个操作更为重要。TreeSet唯一好吃就是可以维持元素的排序;因为排序 所以TreeSet的迭代通常比HashSet快。
对于插入LinkedHashSet要比HashSet代价高;因为要额外维护链表所带来的额外开销。
对Map的选择
除了IdentityHashMap外,所有的Map实现的插入操作都会随着Map尺寸的变大而明显变慢,但是查找操作代价要小得多。
TreeMap通常比HashMap慢,TreeMap是一种创建有序列表的方式。树的行为是:保证有序,并且不必进行特殊排序。一旦填充TreeMap,就可以通过keySet()方法获取键的Set试图,然后调用toArray()形成键的数组。
当使用Map时HahsMap应该是首选,除非需要Map始终保持有序时使用TreeMap。
LinkedHashMap在插入时比HashMap慢一点,因为在维护散列数据结构得同时还要维护链表,也因此迭代速度更快。
IdentityHashMap具有完全不同的性能因为使用== 而不是 equals()来比较元素。
HashMap的性能因子
可以通过手动调整HashMap提高性能,这里有些术语必须了解:
容量:表中的桶位。
初始容量:表在创建时所拥有的桶位数。HashMap和HashSet都可以通过构造函数指定初始化容量。
尺寸:表中当前存储的项数。
负载因子:尺寸/容量。空表的负载因子是0,半满表的负载因子是0.5,负载轻的表冲突可能性小,因此插入和查找更快,迭代则慢一些。HashMap和HashSet都具有指定负载因子的构造器,当负载达到该负载因子水平时,容器会自动增加容量,实现方式是使容量大致加倍,并重新将现有对象分布到新的桶位集中(再散列)。
HashMap使用默认的负载因子是0.75,更高的负载因子会增加查找代价。
十一、实用方法
Collections类(注意不是Collection)内部有很多卓越的的静态方法:
public staticextends Object & Comparable super T>> T max/min(Collection extends T> coll) 返回Collection中最大或最小的元素-采用Collection内置的自然比较法, (Collection extends T> coll, Comparator super T> comp) - 采用Comparator进行比较。 public static int indexOfSubList/lastIndexOfSubList(List> source, List> target) 返回target在source中第一次/最后一次出现的位置,找不到返回-1 public static boolean replaceAll(List list, T oldVal, T newVal) 使用newVal替换oldVal public static void reverse(List> list) 逆转所有元素次序 public static Comparator reverseOrder() 返回一个排序规则 逆转自然顺序 例:TreeSet tr=new TreeSet(Collections.reverseOrder()); public static Comparator reverseOrder(Comparator cmp) 逆转参数的顺序 例:TreeSet tr=new TreeSet(Collections.reverseOrder(new StrLenComparator())); public static void rotate(List> list, int distance) 所有元素向后移动distance个位置,将末尾元素移到前面。 public static void shuffle(List> list) 随机改变指定列表顺序 参数列表:(List> list, Random rnd)时可使用自己的随机机制 public static void sort(List list) 使用List中的自然排序 参数列表:(List list, Comparator super T> c) 时,利用参数中排序规则排序 public static void copy(List super T> dest, List extends T> src) 将src中的元素复制到dest public static void swap(List> list, int i, int j) 替换list中位置i和位置j的元素 public static void fill(List super T> list, T obj) 用元素x替换list中的元素 public static boolean disjoint(Collection> c1, Collection> c2) 两个集合没有任何相同元素时 返回true public static int frequency(Collection> c, Object o) 返回集合中等于o的元素个数 public static int binarySearch(List extends Comparable super T>> list, T key) 在有排序的list中查找key元素的位置 public static List nCopies(int n, T o) 返回大小为n的List,且List不可改变,o为List中元素 emptyList()/emptyMap()/emptySet()返回不可变的空集合 singleton(T t)/singleList(T t)/singleMap(K key,V value) 产生不可变的集合,只包含参数中的元素
设定Collection或Map为不可修改
对“不可修改的”方法调用并不会产生编译时检查,但是完成转换后,任何会改变容器内容的操作都会引起UnsupportedOperationException异常。
不可修改:Collections.unmodifiableXXX(XXX); 比如:Collections.unmodifiableList(new ArrayList(data));
同步
Collections.synchronizedXXX(XXX) 比如:Collections.synchronizedList(new ArrayList(data)) ; 一旦发现两个线程同时操作就会抛出ConcurrentMdificationExcepton