javaforeachjava-streamwrapperconcurrentmodificationexception

nested streams getting - ConcurrentModificationException


So I am trying to do this problem solving interview question where im givin an array of 6,4,7,9,3,12 and trying to return the three values that would add up to the value 13. The catch is that im only using streams to do this problem. No basic. I tried doing it with just .map and flatmap but i ended up having to use one foreach loop due to a void method i am using.

here is the code:

package com.example.demo_david;

import models.Wrapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);

        Integer value = 13;
        List<Integer> fixMe = new ArrayList<>(List.of(6,4,7,9,3,12));
        System.out.println("answer: " + findTheOnesThatEqualTheResult(value, fixMe));
    }

    static List<Integer> findTheOnesThatEqualTheResult(int maxValue, List<Integer> list){
        Wrapper wrapper = new Wrapper();

        List<ArrayList<Integer>> collectedList = list.stream().map(fixMeCurrentNumber ->
                computeLogic(maxValue, wrapper, fixMeCurrentNumber)).collect(Collectors.toList());

        List<Integer> flattenedList = collectedList.stream()
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        return flattenedList;
    }

    private static ArrayList<Integer> computeLogic(int maxValue, Wrapper wrapper, Integer fixMeCurrentNumber) {

        if((wrapper.getSavedNumbers() == null && fixMeCurrentNumber <= maxValue) ||
                (wrapper.getSavedNumbers().size() == 0 && fixMeCurrentNumber <= maxValue )){
            ArrayList<Integer> fixMeList = new ArrayList<>();
            fixMeList.add(fixMeCurrentNumber);
            wrapper.setSavedNumbers(fixMeList);
            wrapper.setCurrentTotalOfList(fixMeCurrentNumber);
        }
        else {
            ArrayList<Integer> copyToBeItereatedOver = new ArrayList<>();
            copyToBeItereatedOver = wrapper.getSavedNumbers();
            copyToBeItereatedOver.stream().forEach(savedNumber -> {
                 validateSavedNumbers(maxValue, wrapper, fixMeCurrentNumber);
            });
        }
        return wrapper.getSavedNumbers();
    }

    private static void validateSavedNumbers(int maxValue, Wrapper wrapper,
                                             Integer fixMeCurrentNumber) {
        if(wrapper.getCurrentTotalOfList() < maxValue &&
                wrapper.getCurrentTotalOfList() + fixMeCurrentNumber <= maxValue) {
            ArrayList<Integer> savedNumbers = new ArrayList<>();
            savedNumbers = wrapper.getSavedNumbers();
            //tried passing a wrapper and still no luck.
            savedNumbers.add(fixMeCurrentNumber);
            //wrapper.setSavedNumbers(savedNumbers);
            //wrapper.setCurrentTotalOfList(wrapper.getCurrentTotalOfList() + fixMeCurrentNumber);
        }
    }
}




---------------

package models;

import lombok.Data;

import java.util.ArrayList;


@Data
public class Wrapper {
    ArrayList<Integer> savedNumbers;
    int currentTotalOfList;
}
---------------

the issue is where I have savedNumbers.add(fixMeCurrentNumber); it then throws the ConcurrentModificationException.

Any suggestions on how you would go about fixing this?

passing a nested object wrapper for the wrapper.savedNumbers - did not work my next guess was to try an itorator - but am trying not to do that as I want to stick to using streams.

Maybe there is a easier way to do it where I do not do nested streams? o.o


Solution

  • In your computeLogic method, you have a Wrapper instance.

    In the body of the else block in that same method, you have:

    copyToBeItereatedOver = wrapper.getSavedNumbers();
    

    However, despite the variable name, it is not a copy. It’s the very same ArrayList that is contained in the Wrapper, and that is the source of your troubles.

    The very next thing your code does is:

    copyToBeItereatedOver.stream().forEach(savedNumber -> {
         validateSavedNumbers(maxValue, wrapper, fixMeCurrentNumber);
    });
    

    You are passing the same Wrapper object to validateSavedNumbers, while using a Stream to iterate over the Wrapper object’s savedNumbers List. And what does validateSavedNumbers do with that Wrapper object? It does this:

    savedNumbers = wrapper.getSavedNumbers();
    savedNumbers.add(fixMeCurrentNumber);
    

    It’s trying to add to the very same List that the computeLogic method is already iterating over. That’s why you’re seeing a ConcurrentModificationException.

    The easiest solution is to make sure copyToBeItereatedOver is actually a copy, as its name implies. Change this:

    copyToBeItereatedOver = wrapper.getSavedNumbers();
    

    to this:

    copyToBeItereatedOver = List.copyOf(wrapper.getSavedNumbers());
    

    (If, for some reason, you’re using an old version of Java, and the compiler doesn’t recognize List.copyOf, you can use copyToBeItereatedOver = new ArrayList<>(wrapper.getSavedNumbers()); instead.)