Search code examples
javaarraylistlambda

arrayList only updates 2 elements when using forEach


I create an arrayList and set all it's elements to 0

When all elements are set to 1, I try to turn them all back to 0 with a lambda and only the first 2 indices are changed.

// create a list freeFrames, size 5, all elements set to 0:
List<Integer> freeFrame = new ArrayList<Integer>(Collections.nCopies(5, 0));

// freeFrame= [0, 0, 0, 0, 0]

// code executes and eventually freeFrame =[1, 1, 1, 1, 1]

// try to change all values to 0 if none are present
if(!freeFrame.contains(0))
    freeFrame.forEach((b) -> freeFrame.set(b, 0));

// freeFrame= [0, 0, 1, 1, 1]

I ended up using a standard loop. Why are only the first 2 indexes getting changed with the above? This post seemed close https://stackoverflow.com/a/20040292/20419870 but I don't think I'm changing the size of the list, just changing the values. This one https://stackoverflow.com/a/56399619/20419870 suggests to use stream and map to safely modify the list while iterating. I had thought only removing and adding to list while iterating was unsafe, does modifying the contents cause an exception I'm not seeing?


Solution

  • There is a misuse of the forEach in your code:

    freeFrame.forEach(b -> freeFrame.set(b, 0));
    

    b here represents the value in the list, but you are using it as if it were the index (List.set wants the index as the first argument).

    Suppose you start with a list l containing [1, 1, 1, 1], this is what happens running your forEach:

    1. Iteration 1:
      • b=1 (first element in the list, not first index!), you execute l.set(1,0)
      • Resulting list [1, 0, 1, 1]
    2. Iteration 2:
      • b=0 (second element in the list, changed at Iteration 1), you execute l.set(0,0)
      • Resulting list [0, 0, 1, 1]
    3. Iteration 3:
      • b=1 (third element in the list), you execute l.set(1,0)
      • Resulting list [0, 0, 1, 1] (nothing changes, it was already 0 from Iteration 1)
    4. Iteration 4:
      • b=1 (fourth element in the list), you execute l.set(1,0)
      • Resulting list [0, 0, 1, 1] (nothing changes, it was already 0 from Iteration 1)

    You can easily replace all the elements in the list with a for:

    for (int i = 0; i < freeFrame.size(); i++)
        freeFrame.set(i, 0);
    

    or using streams, as you already did:

    freeFrame = freeFrame.stream().map(x -> 0).collect(Collectors.toList());
    

    but note that this solution will create a new List instead of modifying the existing list.