Java 8 Streams 可以对集合中的一个项目进行操作,然后将其删除吗?
Can Java 8 Streams operate on an item in a collection, and then remove it?
和几乎所有人一样,我仍在学习新的 Java 8 Streams API 的复杂性(并热爱它们)。我对流的使用有疑问。我将提供一个简化的示例。
Java Streams 允许我们使用 Collection
,并在其上使用 stream()
方法来接收其所有元素的流。其中,有一些有用的方法,例如filter()
、map()
和forEach()
,它们允许我们对内容使用lambda操作。
我的代码看起来像这样(简化):
set.stream().filter(item -> item.qualify())
.map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());
思路是获取集合中所有匹配某个限定符的项的映射,然后通过它们进行操作。手术后,它们不再有任何用途,应从原来的集合中移除。代码运行良好,但我无法动摇 Stream
中有一个操作可以在一行中为我完成此操作。
如果它在 Java 文档中,我可能会忽略它。
有没有更熟悉 API 的人看到类似的东西?
不,您的实现可能是最简单的一个。您可能会通过修改 removeIf
谓词中的状态来做一些非常邪恶的事情,但请不要这样做。另一方面,实际上切换到基于迭代器的命令式实现可能是合理的,这对于这个用例实际上可能更合适和有效。
在一行中没有,但也许你可以利用 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);
它可能更有效,因为它避免了迭代集合两次,一次用于过滤流,另一次用于删除相应的元素。
另外我觉得你的做法比较好
你可以这样做:
set.removeIf(item -> {
if (!item.qualify())
return false;
item.operate();
return true;
});
如果item.operate()
总是returns true
你可以做到非常简洁。
set.removeIf(item -> item.qualify() && item.operate());
但是,我不喜欢这些方法,因为目前还不清楚发生了什么。就个人而言,我会继续为此使用 for
循环和 Iterator
。
for (Iterator<Item> i = set.iterator(); i.hasNext();) {
Item item = i.next();
if (item.qualify()) {
item.operate();
i.remove();
}
}
你真正想做的是对你的集合进行分区。不幸的是,在 Java 8 中,只能通过终端 "collect" 方法进行分区。你最终得到这样的结果:
// 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))
已更新:
上述代码的 Scala 版本
val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
如果我正确理解你的问题:
set = set.stream().filter(item -> {
if (item.qualify()) {
((Qualifier) item).operate();
return false;
}
return true;
}).collect(Collectors.toSet());
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.
您不能使用流从流的源中删除元素。来自 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).
我看到 Paul 在使用流时对清晰度的担忧,这在最上面的答案中有说明。也许添加解释变量可以稍微阐明意图。
set.removeIf(item -> {
boolean removeItem=item.qualify();
if (removeItem){
item.operate();
}
return removeItem;
});
方法有很多种。如果您使用 myList.remove(element),则必须覆盖 equals()。我比较喜欢的是:
allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));
祝你好运,编码愉快:)
和几乎所有人一样,我仍在学习新的 Java 8 Streams API 的复杂性(并热爱它们)。我对流的使用有疑问。我将提供一个简化的示例。
Java Streams 允许我们使用 Collection
,并在其上使用 stream()
方法来接收其所有元素的流。其中,有一些有用的方法,例如filter()
、map()
和forEach()
,它们允许我们对内容使用lambda操作。
我的代码看起来像这样(简化):
set.stream().filter(item -> item.qualify())
.map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());
思路是获取集合中所有匹配某个限定符的项的映射,然后通过它们进行操作。手术后,它们不再有任何用途,应从原来的集合中移除。代码运行良好,但我无法动摇 Stream
中有一个操作可以在一行中为我完成此操作。
如果它在 Java 文档中,我可能会忽略它。
有没有更熟悉 API 的人看到类似的东西?
不,您的实现可能是最简单的一个。您可能会通过修改 removeIf
谓词中的状态来做一些非常邪恶的事情,但请不要这样做。另一方面,实际上切换到基于迭代器的命令式实现可能是合理的,这对于这个用例实际上可能更合适和有效。
在一行中没有,但也许你可以利用 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);
它可能更有效,因为它避免了迭代集合两次,一次用于过滤流,另一次用于删除相应的元素。
另外我觉得你的做法比较好
你可以这样做:
set.removeIf(item -> {
if (!item.qualify())
return false;
item.operate();
return true;
});
如果item.operate()
总是returns true
你可以做到非常简洁。
set.removeIf(item -> item.qualify() && item.operate());
但是,我不喜欢这些方法,因为目前还不清楚发生了什么。就个人而言,我会继续为此使用 for
循环和 Iterator
。
for (Iterator<Item> i = set.iterator(); i.hasNext();) {
Item item = i.next();
if (item.qualify()) {
item.operate();
i.remove();
}
}
你真正想做的是对你的集合进行分区。不幸的是,在 Java 8 中,只能通过终端 "collect" 方法进行分区。你最终得到这样的结果:
// 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))
已更新:
上述代码的 Scala 版本
val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
如果我正确理解你的问题:
set = set.stream().filter(item -> {
if (item.qualify()) {
((Qualifier) item).operate();
return false;
}
return true;
}).collect(Collectors.toSet());
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.
您不能使用流从流的源中删除元素。来自 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).
我看到 Paul 在使用流时对清晰度的担忧,这在最上面的答案中有说明。也许添加解释变量可以稍微阐明意图。
set.removeIf(item -> {
boolean removeItem=item.qualify();
if (removeItem){
item.operate();
}
return removeItem;
});
方法有很多种。如果您使用 myList.remove(element),则必须覆盖 equals()。我比较喜欢的是:
allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));
祝你好运,编码愉快:)