Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to remove object from stream in foreach method?

i have to arrays: arrA and arrB. arrA and arrB are Lists of objectss of diffrent types and add function converts objects A to 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:

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.

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)

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, arrD etc.) but each object can be only on one list

like image 461
Akka Jaworek Avatar asked Sep 12 '16 14:09

Akka Jaworek


Video Answer


2 Answers

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.

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

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.

like image 135
Martin Nyolt Avatar answered Sep 28 '22 10:09

Martin Nyolt


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

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:

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

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

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);
like image 37
davidsheldon Avatar answered Sep 28 '22 11:09

davidsheldon