如何在foreach方法中从流中删除对象?

时间:2016-09-12 14:12:28

标签: java arrays lambda java-8

我必须使用数组:arrAarrBarrAarrB是不同类型的对象列表,add函数将对象A转换为对象B。我想将arrA中的每个对象添加到arrB并从arrA中删除该对象。我试图通过流来做到这一点:

arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);});

当我执行此操作时,发生了两件事:

  1. 并非所有对象都从arrA传递到arrB。
  2. 几次迭代后抛出空指针异常。
  3. 我猜对了,因为每次remove()调用后数组的长度都会减少,并且迭代计数器会增加(只有奇数索引下的对象会传递给arrB

    现在我可以通过在一个流调用中复制数组然后在第二个流调用中删除对象来解决这个问题,但这对我来说似乎不正确。

    这个问题的解决方法是什么?

    EDIT。 附加信息: 在实际实现中,如果先前已过滤

    ,则为此列表
    arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);});
    

    并且几次被调用以将不同条件的元素添加到不同的列表(arrC, arrD等),但每个对象只能在一个列表上

5 个答案:

答案 0 :(得分:15)

Streams旨在以更实用的方式使用,最好将您的集合视为不可变的。

非流式方式是:

arrB.addAll(arrA);
arrA.clear();

但是您可能正在使用Streams,因此您可以过滤输入,因此更像是:

arrB.addAll(arrA.stream().filter(x -> whatever).toList())

然后从arrA中删除(感谢@Holgar的评论)。

arrA.removeIf(x -> whatever)

如果您的谓词很昂贵,那么您可以进行分区:

Map<Boolean, XXX> lists = arrA.stream()
  .collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);

或列出更改:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);

答案 1 :(得分:4)

正如其他人所提到的,foreach无法做到这一点 - 因为for (A a: arrA)循环无法移除元素。

在我看来,最干净的解决方案是使用带有迭代器的while的平面 - 迭代器允许你在迭代时删除元素(只要集合支持它)。

Iterator<A> it = arrA.iterator()
while (it.hasNext()) {
    A a = it.next();
    if (!check(a))
        continue;
    arrB.add(a);
    it.remove();
}

这也使您免于复制/克隆arrA

答案 2 :(得分:1)

我不认为你在迭代它时可以从arrA中删除。

你可以把它包装在一个新的ArrayList&lt;&gt;();

中来解决这个问题

new ArrayList&lt;&gt;(arrA).stream()。foreach(c - &gt; {arrB.add(c); arrA.remove(c);});

答案 3 :(得分:1)

  

我想这是因为每次remove()调用后数组的长度都会减少,并且迭代计数器会增加

右。 for-each-loop就像一个普通的for循环,但更容易编写和读取。你可以把它想象成语法糖。在内部,它将使用Iterator或数组索引。流的forEach方法是一个更加花哨的版本,允许并行执行和函数编码样式but has its own drawbacks

与任何索引循环一样,循环时删除元素会破坏循环。考虑使用索引为0,1和2的三个元素。当您在第一次迭代中删除元素0时,列表项将向上移动一个,下一次迭代将包含元素0(先前为1)和1(之前为2)。您的循环变量现在指向1,因此它会跳过实际的下一个项目。当它到索引2时,你正在处理的循环只剩下一个项目(你删除了两个),这会因为索引超出范围而抛出错误。

可能的解决方案:

  • 使用List方法克隆和清除​​列表。
  • 如果您确实需要在每个项目上调用方法,请使用两个循环。

答案 4 :(得分:-1)

您可以执行 Collections.addAll 。然后当它完成。只需在arrA上调用 clear()