# `collection`接口

> 原文： [https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html](https://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html)

[`Collection`](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html) 表示一组称为其元素的对象。 `Collection`接口用于传递需要最大通用性的对象集合。例如，按照惯例，所有通用集合实现都有一个带有`Collection`参数的构造函数。此构造函数（称为 _ 转换构造函数 _）初始化新集合以包含指定集合中的所有元素，无论给定集合的子接口或实现类型如何。换句话说，它允许你 _ 转换 _ 集合的类型。

例如，假设您有`Collection&lt;String&gt; c`，可能是`List`，`Set`或其他类型的`Collection`。这个习惯用法创建了一个新的`ArrayList`（`List`接口的实现），最初包含`c`中的所有元素。

```
List<String> list = new ArrayList<String>(c);

```

Or — if you are using JDK 7 or later — you can use the diamond operator:

```
List<String> list = new ArrayList<>(c);

```

`Collection`接口包含执行基本操作的方法，例如`int size()`，`boolean isEmpty()`，`boolean contains(Object element)`，`boolean add(E element)`，`boolean remove(Object element)`和`Iterator&lt;E&gt; iterator()`。

它还包含对整个集合进行操作的方法，例如`boolean containsAll(Collection&lt;?&gt; c)`，`boolean addAll(Collection&lt;? extends E&gt; c)`，`boolean removeAll(Collection&lt;?&gt; c)`，`boolean retainAll(Collection&lt;?&gt; c)`和`void clear()`。

还存在用于阵列操作的其他方法（例如`Object[] toArray()`和`&lt;T&gt; T[] toArray(T[] a)`）。

在 JDK 8 及更高版本中，`Collection`接口还公开方法`Stream&lt;E&gt; stream()`和`Stream&lt;E&gt; parallelStream()`，以从底层集合中获取顺序或并行流。 （有关使用流的更多信息，请参阅标题为[聚合操作](../../collections/streams/index.html)的课程。）

考虑到`Collection`代表一组对象，`Collection`接口可以实现您所期望的。它有方法告诉你集合中有多少元素（`size`，`isEmpty`），检查给定对象是否在集合中的方法（`contains`），从集合中添加和删除元素的方法（`add`，`remove`），以及在集合上提供迭代器的方法（`iterator`）。

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

## 遍历集合

遍历集合的方法有三种：（1）使用`for-each`构造的聚合操作（2）和（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)));

```

这些只是您可以对流和聚合操作执行的操作的几个示例。有关更多信息和示例，请参阅标题为[聚合操作](../../collections/streams/index.html)的课程。

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

### for-each Construct

`for-each`构造允许您使用`for`循环简洁地遍历集合或数组 - 请参阅 [for Statement](../../java/nutsandbolts/for.html) 。以下代码使用`for-each`构造在单独的行上打印出集合的每个元素。

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

```

如果需要， [`Iterator`](https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) 是一个对象，它允许您遍历集合并有选择地从集合中删除元素。通过调用`iterator`方法获得集合的`Iterator`。以下是`Iterator`接口。

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

```

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

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

需要时，请使用`Iterator`代替`for-each`构造：

*   删除当前元素。 `for-each`构造隐藏了迭代器，因此您无法调用`remove`。因此，`for-each`构造不可用于过滤。
*   并行迭代多个集合。

以下方法说明如何使用`Iterator`过滤任意`Collection` - 即遍历删除特定元素的集合。

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

```

这段简单的代码是多态的，这意味着无论实现如何，它都适用于 _ 任何 _ `Collection`。此示例演示了使用 Java Collections Framework 编写多态算法是多么容易。

## 集合接口批量操作

_ 批量操作 _ 对整个`Collection`执行操作。您可以使用基本操作实现这些速记操作，但在大多数情况下，此类实现效率较低。以下是批量操作：

*   `containsAll` - 如果目标`Collection`包含指定`Collection`中的所有元素，则返回`true`。
*   `addAll` - 将指定`Collection`中的所有元素添加到目标`Collection`。
*   `removeAll` - 从目标`Collection`中删除也包含在指定`Collection`中的所有元素。
*   `retainAll` - 从目标`Collection`中删除 _ 而非 _ 的所有元素也包含在指定的`Collection`中。也就是说，它仅保留目标`Collection`中也包含在指定`Collection`中的那些元素。
*   `clear` - 删除`Collection`中的所有元素。

如果在执行操作的过程中修改了目标`Collection`，则`addAll`，`removeAll`和`retainAll`方法都返回`true`。

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

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

```

更具体地说，假设您要从`Collection`中删除所有`null`元素。

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

```

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

## 集合接口阵列操作

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

例如，假设`c`是`Collection`。以下片段将`c`的内容转储到新分配的`Object`数组中，其长度与`c`中的元素数相同。

```
Object[] a = c.toArray();

```

假设已知`c`仅包含字符串（可能因为`c`的类型为`Collection&lt;String&gt;`）。以下片段将`c`的内容转储到新分配的`String`数组中，其长度与`c`中的元素数相同。

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

```