集合类操作优化经验总结

在实际的项目开发中会有很多的对象,如何高效、方便地管理对象,成为影响程序性能与可维护性的重要环节。Java 提供了集合框架来解决此类问题,线性表、链表、哈希表等是常用的数据结构,在进行 Java 开发时,JDK 已经为我们提供了一系列相应的类来实现基本的数据结构,所有类都在 java.util 这个包里,清单 1 描述了集合类的关系。

清单 1.集合类之间关系
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

本文讲的就是集合框架的使用经验总结,注意,本文所有代码基于 JDK7。

集合接口

Collection 接口

Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素(Elements)。一些 Collection 允许相同的元素、支持对元素进行排序,另一些则不行。JDK 不提供直接继承自 Collection 的类,JDK 提供的类都是继承自 Collection 的子接口,如 List 和 Set。所有实现 Collection 接口的类都必须提供两个标准的构造函数,无参数的构造函数用于创建一个空的 Collection,有一个 Collection 参数的构造函数用于创建一个新的 Collection,这个新的 Collection 与传入的 Collection 有相同的元素,后一个构造函数允许用户复制一个 Collection。

如何遍历 Collection 中的每一个元素?

不论 Collection 的实际类型如何,它都支持一个 iterator() 的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问 Collection 中每一个元素。典型的用法如下:

Iterator it = collection.iterator(); // 获得一个迭代子

while(it.hasNext()){

Object obj = it.next(); // 得到下一个元素

}

Collection 接口派生的两个接口是 List 和 Set。

Collection 接口提供的主要方法:

  1. boolean add(Object o) 添加对象到集合;
  2. boolean remove(Object o) 删除指定的对象;
  3. int size() 返回当前集合中元素的数量;
  4. boolean contains(Object o) 查找集合中是否有指定的对象;
  5. boolean isEmpty() 判断集合是否为空;
  6. Iterator iterator() 返回一个迭代器;
  7. boolean containsAll(Collection c) 查找集合中是否有集合 C 中的元素;
  8. boolean addAll(Collection c) 将集合 C 中所有的元素添加给该集合;
  9. void clear() 删除集合中所有元素;
  10. void removeAll(Collection c) 从集合中删除 C 集合中也有的元素;
  11. void retainAll(Collection c) 从集合中删除集合 C 中不包含的元素。

List 接口

List 是有序的 Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,这类似于 Java 的数组。和下文要提到的 Set 不同,List 允许有相同的元素。

除了具有 Collection 接口必备的 iterator() 方法外,List 还提供一个 listIterator() 方法,返回一个 ListIterator 接口。和标准的 Iterator 接口相比,ListIterator 多了一些 add() 之类的方法,允许添加、删除、设定元素、向前或向后遍历等功能。实现 List 接口的常用类有 LinkedList,ArrayList,Vector 和 Stack 等。

List 接口提供的主要方法:

  1. void add(int index,Object element) 在指定位置上添加一个对象;
  2. boolean addAll(int index,Collection c) 将集合 C 的元素添加到指定的位置;
  3. Object get(int index) 返回 List 中指定位置的元素;
  4. int indexOf(Object o) 返回第一个出现元素 O 的位置;
  5. Object removeint(int index) 删除指定位置的元素;
  6. Object set(int index,Object element) 用元素 element 取代位置 index 上的元素, 返回被取代的元素。

Map 接口

Map 没有继承 Collection 接口。Map 提供 Key 到 Value 的映射,一个 Map 中不能包含相同的 Key,每个 Key 只能映射一个 Value。Map 接口提供 3 种集合的视图,Map 的内容可以被当作一组 Key 集合,一组 Value 集合,或者一组 Key-Value 映射。

Map 提供的主要方法:

  1. boolean equals(Object o) 比较对象;
  2. boolean remove(Object o) 删除一个对象;
  3. put(Object key,Object value) 添加 key 和 value。

RandomAccess 接口

RandomAccess 接口是一个标志接口,本身并没有提供任何方法,任务凡是通过调用 RandomAccess 接口的对象都可以认为是支持快速随机访问的对象。此接口的主要目的是标识那些可支持快速随机访问的 List 实现。任何一个基于数组的 List 实现都实现了 RaodomAccess 接口,而基于链表的实现则都没有。因为只有数组能够进行快速的随机访问,而对链表的随机访问需要进行链表的遍历。因此,此接口的好处是,可以在应用程序中知道正在处理的 List 对象是否可以进行快速随机访问,从而针对不同的 List 进行不同的操作,以提高程序的性能。

集合类介绍

LinkedList 类

LinkedList 实现了 List 接口,允许 Null 元素。此外 LinkedList 提供额外的 Get、Remove、Insert 等方法在 LinkedList 的首部或尾部操作数据。这些操作使得 LinkedList 可被用作堆栈(Stack)、队列(Queue)或双向队列(Deque)。请注意 LinkedList 没有同步方法,它不是线程同步的,即如果多个线程同时访问一个 List,则必须自己实现访问同步。一种解决方法是在创建 List 时构造一个同步的 List,方法如

List list = Collections.synchronizedList(new LinkedList(…));

ArrayList 类

ArrayList 实现了可变大小的数组。它允许所有元素,包括 Null。Size、IsEmpty、Get、Set 等方法的运行时间为常数,但是 Add 方法开销为分摊的常数,添加 N 个元素需要 O(N) 的时间,其他的方法运行时间为线性。

每个 ArrayList 实例都有一个容量(Capacity),用于存储元素的数组的大小,这个容量可随着不断添加新元素而自动增加。当需要插入大量元素时,在插入前可以调用 ensureCapacity 方法来增加 ArrayList 的容量以提高插入效率。和 LinkedList 一样,ArrayList 也是线程非同步的(unsynchronized)。

ArrayList 提供的主要方法:

  1. Boolean add(Object o) 将指定元素添加到列表的末尾;
  2. Boolean add(int index,Object element) 在列表中指定位置加入指定元素;
  3. Boolean addAll(Collection c) 将指定集合添加到列表末尾;
  4. Boolean addAll(int index,Collection c) 在列表中指定位置加入指定集合;
  5. Boolean clear() 删除列表中所有元素;
  6. Boolean clone() 返回该列表实例的一个拷贝;
  7. Boolean contains(Object o) 判断列表中是否包含元素;
  8. Boolean ensureCapacity(int m) 增加列表的容量,如果必须,该列表能够容纳 m 个元素;
  9. Object get(int index) 返回列表中指定位置的元素;
  10. Int indexOf(Object elem) 在列表中查找指定元素的下标;
  11. Int size() 返回当前列表的元素个数。

Vector 类

Vector 非常类似于 ArrayList,区别是 Vector 是线程同步的。由 Vector 创建的 Iterator,虽然和 ArrayList 创建的 Iterator 是同一接口,但是,因为 Vector 是同步的,当一个 Iterator 被创建而且正在被使用,另一个线程改变了 Vector 的状态(例如,添加或删除了一些元素),这时调用 Iterator 的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。

Stack 类

Stack 继承自 Vector,实现了一个后进先出的堆栈。Stack 提供 5 个额外的方法使得 Vector 得以被当作堆栈使用。除了基本的 Push 和 Pop 方法,还有 Peek 方法得到栈顶的元素,Empty 方法测试堆栈是否为空,Search 方法检测一个元素在堆栈中的位置。注意,Stack 刚创建后是空栈。

Set 类

Set 是一种不包含重复的元素的 Collection,即任意的两个元素 e1 和 e2 都有 e1.equals(e2)=false。Set 最多有一个 null 元素。很明显,Set 的构造函数有一个约束条件,传入的 Collection 参数不能包含重复的元素。请注意,必须小心操作可变对象(Mutable Object),如果一个 Set 中的可变元素改变了自身状态,这可能会导致一些问题。

Hashtable 类

Hashtable 继承 Map 接口,实现了一个基于 Key-Value 映射的哈希表。任何非空(non-null)的对象都可作为 Key 或者 Value。添加数据使用 Put(Key,Value),取出数据使用 Get(Key),这两个基本操作的时间开销为常数。

Hashtable 通过 Initial Capacity 和 Load Factor 两个参数调整性能。通常缺省的 Load Factor 0.75 较好地实现了时间和空间的均衡。增大 Load Factor 可以节省空间但相应的查找时间将增大,会影响像 Get 和 Put 这样的操作