文档

Java™ 教程-Java Tutorials 中文版
Collection 接口
Trail: Collections
Lesson: Interfaces

Collection 接口

Collection 表示一组称为其元素的对象。Collection 接口用于传递需要最大泛型性的对象集合。例如,按照规范,所有泛型集合实现都有一个带有 Collection 参数的构造函数。此构造函数(称为 conversion constructor (转换构造函数))初始化新集合以包含指定集合中的所有元素,无论给定集合的子接口或实现类型如何。换句话说,它允许你 convert (转换) 集合的类型。

例如,假设你有一个 Collection<String> c,它可能是 ListSet,或者是另一种 Collection。这个习惯用法创建一个新的 ArrayList(List 接口的实现),最初包含 c 中的所有元素。

List<String> list = new ArrayList<String>(c);
或者 - 如果你使用的是 JDK 7 或更高版本 - 你可以使用钻石运算符:
List<String> list = new ArrayList<>(c);

Collection 接口包含执行基本操作的方法,例如 int size()boolean isEmpty()boolean contains(Object element)boolean add(E element)boolean remove(Object element)Iterator<E> iterator()

它还包含对整个集合进行操作的方法,例如 boolean containsAll(Collection<?> c)boolean addAll(Collection<? extends E> c)boolean removeAll(Collection<?> c)boolean retainAll(Collection<?> c)void clear()

还存在用于数组操作的其他方法(例如 Object[] toArray()<T> T[] toArray(T[] a)

在 JDK 8 及更高版本中,Collection 接口还公开方法 Stream<E> stream()Stream<E> parallelStream(),从底层集合中获取顺序或并行流。(有关使用流的更多信息,请参阅标题为 Aggregate Operations 的课程。)

如果 Collection 表示一组对象,Collection 接口会执行你的预期。它有方法告诉你集合中有多少元素(sizeisEmpty),有方法检查给定对象是否在集合中(contains),有从集合中添加和移除元素的方法(addremove),以及在集合上提供迭代器的方法(iterator)。

add 方法的定义足够通用,因此对于允许重复的集合以及不重复的集合都有意义。它保证在调用完成后 Collection 将包含指定的元素,并且如果 Collection 因调用而发生更改,则返回 true。类似地,remove 方法旨在从 Collection 中移除指定元素的单个实例,假设它包含要开始的元素,并且如果 Collection 被修改了,则返回 true

遍历集合

遍历集合的方法有三种:(1)使用聚合操作(2)使用 for-each 结构,(3)使用 Iterator

聚合操作

在 JDK 8 及更高版本中,迭代集合的首选方法是获取流并对其执行聚合操作。聚合操作通常与 lambda 表达式结合使用,以使用较少的代码行,使编程更具表现力。以下代码按顺序遍历一组形状并打印出红色对象:

myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));

同样,你可以轻松地请求并行流,如果集合足够大并且你的计算机具有足够的核心,这可能是有意义的:

myShapesCollection.parallelStream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));

使用此 API 收集数据的方法有很多种。例如,你可能希望将 Collection 的元素转换为 String 对象,然后将它们连接起来,用逗号分隔:

    String joined = elements.stream()
    .map(Object::toString)
    .collect(Collectors.joining(", "));

或者累计一下所有员工的工资:

int total = employees.stream()
.collect(Collectors.summingInt(Employee::getSalary)));

这些只是你可以使用流和聚合操作执行的一些示例。有关更多信息和示例,请参阅标题为 Aggregate Operations 的课程。

Collections 框架一直提供许多所谓的“批量操作”作为其 API 的一部分。这些包括对整个集合进行操作的方法,例如 containsAlladdAllremoveAll 等。不要将这些方法与 JDK 8 中引入的聚合操作混淆。新聚合操作与现有批量操作(containsAlladdAll 等)之间的主要区别在于旧版本都是 mutative (变化的),意味着它们都修改了底层集合。相反,新的聚合操作 不会 修改底层集合。使用新的聚合操作和 lambda 表达式时,必须注意避免突变,以免将来引入问题,如果你的代码稍后从并行流运行。

for-each 结构

for-each 结构允许你使用 for 循环简洁地遍历集合或数组 — 参见 The for Statement。以下代码使用 for-each 结构在单独的行上打印出集合的每个元素。

for (Object o : collection)
    System.out.println(o);

迭代器

Iterator 是一个对象,使你可以遍历集合并有选择地从集合中移除元素(如果需要)。通过调用 iterator 方法,你可以获得集合的 Iterator。以下是 Iterator 接口。

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove(); //optional
}

如果迭代对象具有更多元素,则 hasNext 方法返回 truenext 方法返回迭代中的下一个元素。remove 方法从基础 Collection 中移除 next 返回的最后一个元素。每次调用 next 时,只能调用一次 remove 方法,如果违反此规则,则会抛出异常。

请注意,Iterator.remove 是在迭代期间修改集合的 only (唯一) 安全方式;如果在迭代进行过程中以任何其他方式修改基础集合,则行为未指定。

当你需要完成以下操作时,请使用 Iterator 而不是 for-each 构造:

以下方法介绍如何使用 Iterator 过滤任意 Collection — 也就是说,遍历集合移除特定元素。

static void filter(Collection<?> c) {
    for (Iterator<?> it = c.iterator(); it.hasNext(); )
        if (!cond(it.next()))
            it.remove();
}

这段简单的代码是多态的,这意味着无论实现如何,它都适用于 any (任何) Collection。此示例演示了使用 Java 集合框架编写多态算法是多么容易。

集合接口批量操作

Bulk operations (批量操作) 对整个 Collection 执行操作。你可以使用基本操作实现这些速记(译注:shorthand)操作,但在大多数情况下,此类实现效率较低。以下是批量操作:

如果目标 CollectionaddAllremoveAllretainAll 方法执行操作的进程中被修改,则这些方法都返回 true

作为批量操作功能的一个简单示例,请考虑以下习惯用法从 Collection c 中移除指定元素 e所有 实例。

c.removeAll(Collections.singleton(e));

更具体地说,假设你要从 Collection 中移除所有 null 元素。

c.removeAll(Collections.singleton(null));

这个习惯用法使用 Collections.singleton,这是一个静态工厂方法,它返回一个只包含指定元素的不可变 Set

集合接口数组操作

toArray 方法是作为集合和旧 API 之间的桥梁提供的,这些 API 在输入时期望数组。数组操作允许将 Collection 的内容转换为数组。不带参数的简单形式会创建一个新的 Object 数组。更复杂的形式允许调用者提供数组或选择输出数组的运行时类型。

例如,假设 cCollection。以下代码段将 c 的内容转储到新分配的 Object 数组中,其长度与 c 中的元素数相同。

Object[] a = c.toArray();

假设 c 仅包含字符串(可能因为 c 的类型为 Collection<String>)。以下代码段将 c 的内容转储到新分配的 String 数组中,其长度与 c 中的元素数相同。

String[] a = c.toArray(new String[0]);

Previous page: Interfaces
Next page: The Set Interface