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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39452411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:09:54  来源:igfitidea点击:

how to remove object from stream in foreach method?

javaarrayslambdajava-8

提问by Akka Jaworek

i have to arrays: arrAand arrB. arrAand arrBare Lists of objectss of diffrent types and addfunction converts objects Ato objects B. I want to add each object from arrA to arrB and remove that object from arrA. Im trying to do this by stream:

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

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

when i execute this, two things are happening:

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

  1. not all objects are passed from arrA to arrB.
  2. after few iterations null pointer exception is thrown.
  1. 并非所有对象都从 arrA 传递到 arrB。
  2. 几次迭代后抛出空指针异常。

i gues it's because length of array is decreased after each remove()call and the counter of iterations is increased (only objects under odd indexes are passed to arrB)

我想这是因为每次remove()调用后数组的长度都会减少,并且迭代的计数器会增加(只有奇数索引下的对象会被传递给arrB

Now i could solve this by copying array in one stream call and then remove objects in second stream call but this doesnt seem correct for me.

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

What would be proper solution to this problem?

这个问题的正确解决方案是什么?

EDIT. Additional information: in real implementation this list if previously filtered

编辑。附加信息:在实际实施中,此列表如果先前已过滤

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

and its called few times to add elements meeting diffrent conditions to diffrent lists (arrC, arrDetc.) but each object can be only on one list

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

采纳答案by davidsheldon

Streams are designed to be used in a more functional way, preferably treating your collections as immutable.

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

The non-streams way would be:

非流方式将是:

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

However you might be using Streams so you can filter the input so it's more like:

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

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

then remove from arrA (thanks to @Holgar for the comment).

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

arrA.removeIf(x -> whatever)

If your predicate is expensive, then you could partition:

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

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

or make a list of the changes:

或列出更改:

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

回答by Mike Samaras

I don't think you can remove from arrA while you iterate over it.

我认为您不能在迭代时从 arrA 中删除它。

You can get around this by wrapping it in a new ArrayList<>();

你可以通过将它包装在一个新的 ArrayList<>(); 中来解决这个问题。

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

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

回答by Hubert Grzeskowiak

i guess it's because length of array is decreased after each remove() call and the counter of iterations is increased

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

Right. the for-each-loop is just like a normal for-loop, but easier to write and read. You can think of it as syntactic sugar. Internally it will either use an Iterator or array indices. The forEachmethod of streams is a more fancy version of it that allows parallel execution and functional coding style, but has its own drawbacks.

对。for-each-loop 就像一个普通的 for 循环,但更容易编写和阅读。您可以将其视为语法糖。在内部,它将使用迭代器或数组索引。forEach流的方法是它的一个更奇特的版本,它允许并行执行和函数式编码风格,但有其自身的缺点

As with any indexed loop, removing an element while looping breaks the loop. Consider having three elements with indices 0, 1, and 2. When you remove element 0 in the first iteration, the list items will shift one up and the next iteration you'll have elements 0 (previously 1) and 1 (previously 2). Your loop variable now points to 1, so it skips the actually next item. When it gets to index 2 the loop you're working on only has one item left (you removed two), which throws an error because the index is out of bounds.

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

Possible solutions:

可能的解决方案:

  • Use the Listmethods for cloning and clearing lists.
  • Do it with two loops if you really need to call the methods on each single item.
  • 使用List克隆和清除​​列表的方法。
  • 如果您确实需要在每个单个项目上调用方法,请使用两个循环来执行此操作。

回答by Martin Nyolt

As the others have mentioned, this is not possible with foreach- as it is impossible with the for (A a: arrA)loop to remove elements.

正如其他人所提到的,这是不可能的foreach- 因为for (A a: arrA)循环不可能删除元素。

In my opinion, the cleanest solution is to use a plain for whilewith iterators - iterators allow you to remove elements while iterating (as long as the collection supports that).

在我看来,最干净的解决方案是对while迭代器使用普通的 for - 迭代器允许您在迭代时删除元素(只要集合支持)。

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

This also saves you from copying/cloning arrA.

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

回答by Jacob

You could just do Collections.addAll. Then when that's finished. just call clear()on arrA.

你可以只做Collections.addAll。然后当它完成。只需在 arrA 上调用clear()即可。