1. 概述

在本篇文章中,我们将介绍几种在 Java 8 Stream 中对集合中的元素执行操作,并在操作完成后将其从原集合中移除的方法。

虽然 Stream API 主要用于处理不可变的数据流,但有时我们仍需要在操作后将某些元素从原始集合中清除。这篇文章将围绕这一场景,提供几种实用且高效的实现方式。

2. 准备工作

首先我们定义一个简单的 Item 类,它只有一个 int 类型的字段,并包含两个方法:

  • isQualified():判断该对象是否满足操作条件
  • operate():执行具体操作逻辑
class Item {
    private int value;

    // constructors

    public boolean isQualified() {
        return value % 2 == 0;
    }

    public void operate() {
        System.out.println("Even Number");
    }
}

然后我们初始化一个包含多个 Item 的列表作为数据源:

List<Item> itemList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    itemList.add(new Item(i));
}

3. 元素过滤

在很多场景下,如果只是想对部分元素进行操作而不需要从原集合中移除它们,使用 Stream#filter 就足够了:

itemList.stream()
  .filter(item -> item.isQualified())
  ...

这种方式简单粗暴,适用于大多数只读操作。

4. 执行操作并移除元素

4.1. 使用 Collection.removeIf

这是最推荐的方式之一,直接利用 Stream 进行操作后再调用 removeIf 移除符合条件的元素。

我们可以先创建一个 Predicate 来判断哪些元素是“合格”的:

Predicate<Item> isQualified = item -> item.isQualified();

接着对这些元素执行操作:

itemList.stream()
  .filter(isQualified)
  .forEach(item -> item.operate());

操作完成后,使用相同的 Predicate 调用 removeIf 方法即可从原集合中移除这些元素:

itemList.removeIf(isQualified);

⚠️ 注意:removeIf 内部使用的是 Iterator 实现,因此它是线程安全的(在单线程环境下),并且能避免并发修改异常。

4.2. 使用 Collection.removeAll

如果你不想用 Predicate,也可以先将已处理过的元素收集到另一个列表中,最后通过 removeAll 一次性移除:

List<Item> operatedList = new ArrayList<>();
itemList.stream()
  .filter(item -> item.isQualified())
  .forEach(item -> {
    item.operate();
    operatedList.add(item);
});
itemList.removeAll(operatedList);

⚠️ 注意:removeAll 底层使用的是普通的 for 循环匹配元素,效率略低于 removeIf,尤其在大数据量时要谨慎使用。

5. 总结

本文介绍了如何在 Java 8 Stream 中对集合元素执行操作并将其从原集合中移除的几种常见方式:

  • ✅ 最推荐:使用 removeIf 结合 Predicate
  • ⚠️ 备选方案:使用 removeAll 配合额外的 List 存储已操作元素

两种方式各有优劣,前者更高效简洁,后者语义清晰但性能稍差。

完整代码和测试用例可以在 GitHub 仓库 中找到。


原始标题:Operating on and Removing an Item from Stream