Java 8 Streams可以对集合中的项目进行操作,然后将其删除吗?

时间:2015-05-04 22:39:03

标签: java collections java-8 filtering java-stream

就像每个人一样,我仍然在学习新的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中有一个操作可以为我做这个操作,只需一行。

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

是否有人更熟悉API会看到类似的内容?

8 个答案:

答案 0 :(得分:117)

你可以这样做:

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

答案 1 :(得分:4)

在一行中没有,但也许你可以使用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);

它可能更有效率,因为它避免迭代集合两次,一次用于过滤流,然后一次用于删除相应的元素。

否则我认为你的方法相对较好。

答案 2 :(得分:4)

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

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

祝你好运,编码愉快:)

答案 3 :(得分:2)

您真正想要做的是对您的设置进行分区。不幸的是,在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))

更新:

上面代码的Scala版本

val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`

答案 4 :(得分:1)

不,你的实施可能是最简单的。你可以通过修改r.js谓词中的状态来做一些非常邪恶的事情,但请不要。另一方面,实际切换到基于迭代器的命令式实现可能是合理的,这对于这个用例实际上可能更合适和有效。

答案 5 :(得分:1)

如果我理解你的问题:

set = set.stream().filter(item -> {
    if (item.qualify()) {
        ((Qualifier) item).operate();
        return false;
    }
    return true;
}).collect(Collectors.toSet());

答案 6 :(得分:1)

  

操作后,它们不再用于其他目的,应从原始组中删除。代码效果很好,但是我不能动摇Stream中的一个操作,可以在一行中为我做这个。

您无法使用流从流的源中删除元素。来自Javadoc

  

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

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

答案 7 :(得分:0)

我在最佳答案中说明了使用流时保罗的清晰度。也许添加解释变量可以稍微澄清一下意图。

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