使用 Java 流删除和收集元素

2022-09-02 23:49:41

假设我有一个 ,和一个与我要从 中删除的元素匹配。但我不仅想丢弃它们,我还想将匹配的元素移动到新集合中。我会在Java 7中做这样的事情CollectionPredicateCollection

List<E> removed = new LinkedList<>();
for (Iterator<E> i = data.iterator(); i.hasNext();) {
    E e = i.next();
    if (predicate.test(e)) {
        removed.add(e);
        i.remove();
    }
}

我很好奇是否有流/ Java 8方法来做到这一点。不幸的是,Collections.removeIf() 只是返回一个(甚至没有计算已删除元素的数量?太糟糕了。我设想了这样的东西(虽然当然不存在):boolean.removeAndYield(Predicate)

List<E> removed = data.removeAndYield(predicate).collect(Collectors.toList());

注意:这个问题的灵感来自一个类似的问题;这个问题是关于在从集合中删除的项目上获取流的更一般的情况。正如链接的问题中指出的那样,命令式解决方案可能更具可读性,但我很好奇这是否适用于流。

编辑:显然,我们可以将任务分为两个独立的步骤,并且假设适当的数据结构将是有效的。问题是,这是否可以在任意集合上完成(可能没有效率等)。.contains()


答案 1

如果你不介意,让我稍微改变一下你的要求。:-)

所需结果的一个特征是,匹配的元素应最终位于一个集合中,而不匹配的元素应最终位于另一个集合中。在 Java-8 之前的突变世界中,考虑获取不匹配元素集合的最简单方法是从原始集合中删除匹配的元素。

但是,删除 - 修改原始列表 - 是要求的固有部分吗?

如果不是,则可以通过简单的分区操作实现结果:

Map<Boolean, List<E>> map = data.stream().collect(partitioningBy(predicate));

结果映射本质上是两个列表,它们包含匹配(键 = true)和不匹配(键 = 假)元素。

优点是这种技术可以在一次通过中完成,并在必要时并行完成。当然,与从原始元素中删除匹配项相比,这会创建不匹配元素的重复列表,但这是为不可变性付出的代价。权衡可能是值得的。


答案 2

我会保持简单:

Set<E> removed = set.stream()
    .filter(predicate)
    .collect(Collectors.toSet());

set.removeAll(removed);

推荐