Java 8 Streams 可以对集合中的项目进行操作,然后将其删除吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30041206/
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
Can Java 8 Streams operate on an item in a collection, and then remove it?
提问by Michael Eric Oberlin
Like just about everyone, I'm still learning the intricacies (and loving them) of the new Java 8 Streams API. I have a question concerning usage of streams. I'll provide a simplified example.
和几乎所有人一样,我仍在学习新 Java 8 Streams API 的复杂性(并热爱它们)。我有一个关于流使用的问题。我将提供一个简化的示例。
Java Streams allows us to take a Collection
, and use the stream()
method on it to receive a stream of all of its elements. Within it, there are a number of useful methods, such as filter()
, map()
, and forEach()
, which allow us to use lambda operations on the contents.
Java Streams 允许我们获取一个Collection
,并使用stream()
它的方法来接收其所有元素的流。在这,有许多有用的方法,比如filter()
,map()
和forEach()
,这让我们对内容的使用拉姆达操作。
I have code that looks something like this (simplified):
我的代码看起来像这样(简化):
set.stream().filter(item -> item.qualify())
.map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());
The idea is to get a mapping of all items in the set, which match a certain qualifier, and then operate through them. After the operation, they serve no further purpose, and should be removed from the original set. The code works well, but I can't shake the feeling that there's an operation in Stream
that could do this for me, in a single line.
其思想是获取集合中所有项的映射,匹配某个限定符,然后通过它们进行操作。操作后,它们不再有任何用途,应从原始集合中删除。代码运行良好,但我无法摆脱这样一种感觉,即有一个操作Stream
可以在一行中为我完成此操作。
If it's in the Javadocs, I may be overlooking it.
如果它在 Javadocs 中,我可能会忽略它。
Does anyone more familiar with the API see something like that?
有没有更熟悉 API 的人看到类似的东西?
采纳答案by Paul Boddington
You can do it like this:
你可以这样做:
set.removeIf(item -> {
if (!item.qualify())
return false;
item.operate();
return true;
});
If item.operate()
always returns true
you can do it very succinctly.
如果item.operate()
总是返回,true
您可以非常简洁地完成。
set.removeIf(item -> item.qualify() && item.operate());
However, I don't like these approaches as it is not immediately clear what is going on. Personally, I would continue to use a for
loop and an Iterator
for this.
但是,我不喜欢这些方法,因为尚不清楚发生了什么。就个人而言,我会继续使用for
循环和一个Iterator
。
for (Iterator<Item> i = set.iterator(); i.hasNext();) {
Item item = i.next();
if (item.qualify()) {
item.operate();
i.remove();
}
}
回答by Louis Wasserman
Nope, your implementation is probably the simplest one. You might do something deeply evil by modifying state in the removeIf
predicate, but please don't. On the other hand, it might be reasonable to actually switch to an iterator-based imperative implementation, which might actually be more appropriate and efficient for this use case.
不,您的实现可能是最简单的。您可能会通过修改removeIf
谓词中的状态来做一些非常邪恶的事情,但请不要这样做。另一方面,实际上切换到基于迭代器的命令式实现可能是合理的,这对于这个用例实际上可能更合适和更有效。
回答by Alexis C.
In one line no, but maybe you could make use of the partitioningBy
collector:
在一行中不,但也许您可以使用partitioningBy
收集器:
Map<Boolean, Set<Item>> map =
set.stream()
.collect(partitioningBy(Item::qualify, toSet()));
map.get(true).forEach(i -> ((Qualifier)i).operate());
set = map.get(false);
It might be more efficient as it avoids iterating the set two times, one for filtering the stream and then one for removing corresponding elements.
它可能更有效,因为它避免了两次迭代集合,一次用于过滤流,然后一次用于删除相应的元素。
Otherwise I think your approach is relatively fine.
否则我认为你的方法比较好。
回答by Dmitriy Yefremov
What you really want to do is to partition your set. Unfortunately in Java 8 partitioning is only possible via the terminal "collect" method. You end up with something like this:
你真正想做的是对你的集合进行分区。不幸的是,在 Java 8 中分区只能通过终端“收集”方法进行。你最终会得到这样的结果:
// test data set
Set<Integer> set = ImmutableSet.of(1, 2, 3, 4, 5);
// predicate separating even and odd numbers
Predicate<Integer> evenNumber = n -> n % 2 == 0;
// initial set partitioned by the predicate
Map<Boolean, List<Integer>> partitioned = set.stream().collect(Collectors.partitioningBy(evenNumber));
// print even numbers
partitioned.get(true).forEach(System.out::println);
// do something else with the rest of the set (odd numbers)
doSomethingElse(partitioned.get(false))
Updated:
更新:
Scala version of the code above
上面代码的Scala版本
val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
回答by user_3380739
if I understand your question correctly:
如果我正确理解你的问题:
set = set.stream().filter(item -> {
if (item.qualify()) {
((Qualifier) item).operate();
return false;
}
return true;
}).collect(Collectors.toSet());
回答by user_3380739
After the operation, they serve no further purpose, and should be removed from the original set. The code works well, but I can't shake the feeling that there's an operation in Stream that could do this for me, in a single line.
操作后,它们不再有任何用途,应从原始集合中删除。代码运行良好,但我无法摆脱 Stream 中有一个操作可以在一行中为我完成此操作的感觉。
You cannot remove elements from the source of the stream with the stream. From the Javadoc:
您不能使用流从流的源中删除元素。从Javadoc:
Most stream operations accept parameters that describe user-specified behavior..... To preserve correct behavior, these behavioral parameters:
- must be non-interfering (they do not modify the stream source); and
- in most cases must be stateless (their result should not depend on any state that might change during execution of the stream pipeline).
大多数流操作接受描述用户指定行为的参数......为了保持正确的行为,这些行为参数:
- 必须是无干扰的(它们不修改流源);和
- 在大多数情况下必须是无状态的(它们的结果不应依赖于在流管道执行期间可能发生变化的任何状态)。
回答by vacant78
I see Paul's clarity concern when using streams, stated in the top answer. Perhaps adding explaining variable clarifies intentions a little bit.
我看到保罗在使用流时对清晰度的关注,如最上面的答案所述。也许添加解释变量可以稍微澄清意图。
set.removeIf(item -> {
boolean removeItem=item.qualify();
if (removeItem){
item.operate();
}
return removeItem;
});
回答by panayot_kulchev_bg
There are many approaches. If you use myList.remove(element) you must override equals(). What I prefer is:
有很多方法。如果使用 myList.remove(element),则必须覆盖 equals()。我更喜欢的是:
allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));
Good luck and happy coding :)
祝你好运,编码愉快:)