可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 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.
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.
If it's in the Javadocs, I may be overlooking it.
Does anyone more familiar with the API see something like that?
回答1:
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.
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<Item> i = set.iterator(); i.hasNext();) {
Item item = i.next();
if (item.qualify()) {
item.operate();
i.remove();
}
}
回答2:
In one line no, but maybe you could make use of the partitioningBy
collector:
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.
回答3:
There is many approaches. If use myList.remove(element) must override equals(). Second what I prefer is:
allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));
Good luck and happy coding :)
回答4:
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:
// 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
val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
回答5:
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.
回答6:
if I understand your question correctly:
set = set.stream().filter(item -> {
if (item.qualify()) {
((Qualifier) item).operate();
return false;
}
return true;
}).collect(Collectors.toSet());
回答7:
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.
You cannot remove elements from the source of the stream with the stream. From the 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).
回答8:
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;
});