一个集合表示一组对象。Collection接口被用来传递对象的集合,具有最强的通用性。例如,默认所有的集合实现都有一个构造器带有一个Collection类型参数。这个构造器被称作转换构造器,用指定集合的元素来初始化新集合,而不管指定集合实现的是哪个集合接口和实现类型。换句话说,这个构造器使得我们可以转化集合的类型(比如List到Set,Set到List)。
设想一下,假如你有一个Collection<String> c,它可能是一个List,也可能是一个Set,或者其他类型的Collection。按照上面的方法可以这样初始化一个ArrayList包含c中所有元素:
List<String> list = new ArrayList<String>();
下面是Collection接口实现:
public interface Collection<E> extends Iterable<E> {
// Basic operations
int size();
boolean isEmpty();
boolean contains(Object element);
// optional
boolean add(E element);
// optional
boolean remove(Object element);
Iterator<E> iterator();
// Bulk operations
boolean containsAll(Collection<?> c);
// optional
boolean addAll(Collection<? extends E> c);
// optional
boolean removeAll(Collection<?> c);
// optional
boolean retainAll(Collection<?> c);
// optional
void clear();
// Array operations
Object[] toArray();
<T> T[] toArray(T[] a);
}
这个接口做了我们期望一组对象能做的事情。这个接口通过size、isEmpty方法来告诉我们集合中还有多少元素。通过contains方法来判断给定元素是否在集合中。通过add、remove方法来添加或移除元素。并且提供iterator方法来迭代访问集合。
add方法足够泛化,使得它既支持允许重复元素的集合,又支持不允许重复元素的集合。它保证集合在调用add方法后将包含指定元素,并且返回true,如果集合在调用add之后改变了。类似地,remove方法用来从集合中移除指定元素,前提是集合包含这个元素,并且返回true如果调用remove方法之后集合改变了。
遍历集合
有两种方法遍历集合:
- for-each
- 迭代器访问
fo-each:
for-each访问如下:
for (Object o : collection)
System.out.println(o);
迭代器访问:
一个迭代器使得我们可以遍历集合并且可以有选择性地移除集合中的某些元素。我们可以通过调用集合的iterator方法来获得集合的迭代器。下面是Iterator接口:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); //optional
}
hasNext方法指示集合中是否还有元素,next返回迭代中的下一个元素。remove方法移除next返回的元素。每调用一次next,最多只能调用一次remove,否则会抛出异常。比如下面代码遍历集合a,并且删除它的所有元素:
List<String> a = new ArrayList<String>();
a.add("hello");
a.add("world");
Iterator<String> iter = a.iterator();
while(iter.hasNext()) {
System.out.print(iter.next() + "\t");
iter.remove();
}
上面如果把iter.remove放到iter.next()之前,就会抛出java.lang.IllegalStateException异常。
注意Iterator.remove是遍历集合期间修改集合的唯一安全方法。如果在遍历集合期间用其他方法修改集合,会发生难以预料的事情。
使用迭代器而非for-each当你需要做下面的事情时: - 移除当前元素。for-each不是用来过滤集合中元素的好方法。应该用iterator来过滤集合中的元素,代码如下:
static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); )
if (!condition(it.next()))
it.remove();
}
这段简单的代码体现了多态,这意味着它可以支持所有的集合,而不管它的具体实现如何。这个例子也证明了利用Java集合框架编写多态算法是多么的简单。
集合接口批量操作
批量操作对整个集合进行某种操作。我们可以采用其他的基本操作实现对整个集合的操作,虽然那样做可能不够高效。下面是集合接口支持的批量操作:
- containsAll — 返回true,如果集合包含指定集合中的所有元素
- addAll — 将指定集合中的所有元素添加到目标集合中
- removeAll — 从目标集合中移除所有也在指定集合中存在的元素,即从目标集合中移除交集元素
- retainAll — 只保留目标集合和指定集合都含有的元素,即交集元素。如果执行retainALl操作后,目标集合被修改,则返回true。那么什么时候返回false呢?当目标集合含有的元素与指定集合完全一样时。
- clear - 清空目标集合中的所有元素。
上面几个方法返回true,当目标集合在执行这些方法之后被修改了。
举一个简单的例子来证明批量操作的威力,考虑下面的代码,它用来从集合中移除指定元素e:
c.removeAll(Collections.singleton(e));
再举一个更加特殊的例子,如果你想移除集合中的所有null元素:
c.removeAll(Collections.singleton(null));
这里使用了Collections.singleton,这是一个静态工厂方法,返回结果是一个只包含指定元素的不变Set。
集合接口的数组操作
toArray方法被用来沟通collections和旧API(期望一个数组作为输入)。数组操作使得集合被转换为数组。不带任何参数的toArray方法创建一个对象的新数组。更复杂的形式允许指定一个数组参数或者为返回结果选择一个运行时类型。
例如,设想c是一个Collection。下面的代码将c的内容填充到一个新开辟的对象数组中,这个数组的长度等于c的元素数目:
Object[] a = c.toArray();
又假设c只包含字符串,即Collection<String> c。那么下面的代码将返回一个字符串数组,数组的长度还是等于c的元素数目:
String[] a = c.toArray(new String[0]);