使用 Java 流删除和收集元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30042222/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Remove and collect elements with Java streams
提问by dimo414
Suppose I have a Collection
, and a Predicate
that matches elements I'd like to remove from the Collection
. But I don't just want to discard them, I want to move the matched elements into a new collection. I'd do something like thisin Java 7:
假设我有一个Collection
, 和一个Predicate
匹配我想从Collection
. 但我不只是想丢弃它们,我想将匹配的元素移动到一个新集合中。我会做一些像这样在Java 7中:
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();
}
}
I'm curious if there's a streams / Java 8 way to do it. Collections.removeIf()
unfortunately simply returns a boolean
(not even a count of the number of removed elements? Too bad.) I envision something like this (though of course .removeAndYield(Predicate)
doesn't exist):
我很好奇是否有流/Java 8 的方式来做到这一点。 Collections.removeIf()
不幸的是,它只是返回一个boolean
(甚至不是已删除元素的数量?太糟糕了。)我设想了这样的事情(尽管当然.removeAndYield(Predicate)
不存在):
List<E> removed = data.removeAndYield(predicate).collect(Collectors.toList());
Note: this question was inspired by a similar question; this question is about the more general case of getting a stream over the items removed from a collection. As pointed out in the linked question, the imperative solution may be more readable, but I'm curious if this is even possible with streams.
注意:这个问题的灵感来自一个类似的问题;这个问题是关于从集合中删除的项目上获取流的更一般情况。正如链接问题中所指出的,命令式解决方案可能更具可读性,但我很好奇这是否适用于流。
Edit:Clearly we can split the task into two separate steps, and assuming the appropriate data structures it will be efficient. The question is can this be done on arbitrary collections (which may not have efficient .contains()
etc.).
编辑:显然我们可以将任务分成两个单独的步骤,并假设适当的数据结构将是有效的。问题是这可以在任意集合上完成(可能没有效率.contains()
等)。
回答by Misha
I'd keep it simple:
我会保持简单:
Set<E> removed = set.stream()
.filter(predicate)
.collect(Collectors.toSet());
set.removeAll(removed);
回答by Paul Boddington
If you want a functional way to do this, you could write your own method.
如果您想要一种功能性的方法来做到这一点,您可以编写自己的方法。
static <E> Set<E> removeIf(Collection<? extends E> collection, Predicate<? super E> predicate) {
Set<E> removed = new HashSet<>();
for (Iterator<? extends E> i = collection.iterator(); i.hasNext();) {
E e = i.next();
if (predicate.test(e)) {
removed.add(e);
i.remove();
}
}
return removed;
}
This could be used to remove all odd numbers from a List
.
这可用于从List
.
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Set<Integer> removed = removeIf(set, i -> i % 2 != 0);
System.out.println(set);
System.out.println(removed);
回答by Stuart Marks
If you don't mind, let me bend your requirements a little bit. :-)
如果你不介意,让我稍微弯曲一下你的要求。:-)
One characteristic of the desired result is that the matching elements should end up in one collection, and the non-matching elements should end up in a different collection. In the pre-Java-8 mutative world, the easiest way to think about getting a collection of non-matching elements is to removethe matching elements from the original collection.
期望结果的一个特征是匹配元素应该在一个集合中结束,而不匹配元素应该在不同的集合中结束。在 Java-8 之前的可变世界中,考虑获取非匹配元素集合的最简单方法是从原始集合中删除匹配元素。
But is removal -- modification of the original list -- an intrinsic part of the requirement?
但是删除——修改原始列表——是需求的内在部分吗?
If it isn't, then the result can be achieved via a simple partitioning operation:
如果不是,那么结果可以通过一个简单的分区操作来实现:
Map<Boolean, List<E>> map = data.stream().collect(partitioningBy(predicate));
The result map is essentially two lists, which contain the matching (key = true) and non-matching (key = false) elements.
结果映射本质上是两个列表,其中包含匹配 (key = true) 和非匹配 (key = false) 元素。
The advantage is that this technique can be done in one pass and in parallel if necessary. Of course, this creates a duplicate list of non-matching elements compared to removing the matches from the original, but this is the price to pay for immutability. The tradeoffs might be worth it.
优点是这种技术可以一次性完成,必要时可以并行完成。当然,与从原始元素中删除匹配项相比,这会创建一个不匹配元素的重复列表,但这是为不变性付出的代价。权衡可能是值得的。
回答by Dr. Max V?lkel
Just write yourself a reusable function like this:
只需为自己编写一个可重用的函数,如下所示:
/**
* Removes all matching element from iterator
*
* @param it
* @param predicate
*/
public static <E> void removeMatching(final Iterator<E> it, final Predicate<E> predicate) {
while (it.hasNext()) {
final E e = it.next();
if (predicate.test(e)) {
it.remove();
}
}
}
I have also not found a pre-existing solution for Streams. Using iterator.remove() requires less memory than a temporary set.
我也没有找到一个预先存在的 Streams 解决方案。使用 iterator.remove() 比临时集需要更少的内存。