【发布时间】:2023-03-25 09:41:01
【问题描述】:
假设我有一个Collection 和一个Predicate,它们与我想从Collection 中删除的元素相匹配。但我不只是想丢弃它们,我想将匹配的元素移动到一个新的集合中。我会在 Java 7 中执行类似 this 的操作:
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());
注意:这个问题的灵感来自a similar question;这个问题是关于从集合中删除的项目上获取流的更一般情况。正如链接问题中所指出的,命令式解决方案可能更具可读性,但我很好奇这是否甚至可以通过流实现。
编辑: 显然,我们可以将任务分成两个独立的步骤,并假设适当的数据结构将是有效的。问题是这可以在任意集合上完成(可能没有有效的.contains() 等)。
【问题讨论】:
-
困难在于你不能在没有
ConcurrentModificationException的情况下迭代它时从Set中删除元素,因此任何Java 8 方式都需要至少两次迭代,如Misha 的回答。 AFAIK 通过set的单次迭代来做到这一点的唯一方法是使用显式的Iterator。 -
您可以在
Collection上使用Iterator,这表明在概念上可以使用Stream。显然,这并不意味着它是开箱即用的,但它可能并不复杂。或者也许有一个很好的理由它在 JDK 中不可用。 -
我的问题中的“Java 7”示例演示了在迭代过程中删除元素。如果我们可以通过命令式的方式一次性完成,是否可以在功能上完成?
-
@dimo414 如果您关心性能,请记住,最常用的集合 (
ArrayList) 在迭代解决方案下的扩展性非常差——从中间删除元素的成本很高数组通过迭代器一次一个。
标签: java java-8 java-stream