Java8Streams 是否可以对集合中的某个项进行操作,然后将其删除?

和几乎所有人一样,我仍然在学习新的 Java8StreamsAPI 的复杂性(并且喜欢它们)。我有一个关于数据流使用的问题。我将提供一个简化的例子。

JavaStreams 允许我们获取一个 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中有一个操作可以为我做到这一点,在一个单一的行。

如果是在 Javadocs 我可能会忽略它。

有没有更熟悉 API 的人看到类似的东西?

91152 次浏览

不,您的实现可能是最简单的一个。通过修改 removeIf谓词中的 state,您可能会做一些非常邪恶的事情,但是请不要这样做。另一方面,实际切换到基于迭代器的命令式实现可能是合理的,对于这个用例来说,实际上可能更合适、更有效。

在一行中没有,但是也许你可以利用 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()总是返回 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();
}
}

您真正需要做的是对集合进行分区。不幸的是,在 Java8中,分区只能通过终端“ Collection”方法实现。你会得到这样的结果:

// 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());

在操作之后,它们没有进一步的用途,应该从原始集中删除。代码工作得很好,但我不能动摇的感觉是,在 Stream 中有一个操作可以为我做到这一点,在一个单一的行。

不能用流从流的源中删除元素。从 贾瓦多克:

大多数流操作接受描述用户指定行为的参数... ... 为了保持正确的行为,这些行为参数:

  • 必须是非干扰的(他们不修改流源) ; 和
  • 在大多数情况下必须是无状态的(它们的结果不应该依赖于流管道执行期间可能更改的任何状态)。

我看到了 Paul 在使用流时的清晰关注,在最上面的答案中陈述了这一点。也许添加解释变量可以稍微澄清一下意图。

set.removeIf(item -> {
boolean removeItem=item.qualify();
if (removeItem){
item.operate();
}
return removeItem;
});

有很多方法。如果你使用 myList.move (element) ,你必须覆盖 equals ()。我更喜欢的是:

allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));

祝你好运,编码愉快:)