How to replace the indicated values ​of a set with values ​drawn from the list

I have a complex obj object that contains a CountryUnit object that holds a collection of countries. countries has the following countryValue values:

countries == {
                Country1 (countryValue == "A")
                Country2 (countryValue == "B")
             }

In this case, I need to do a transformation so that the resulting set countries contains the values for countryValue which will be replaced with the randomly selected values from allowedCountryList. For example:

countries == {
                Country1 (countryValue == "N")
                Country2 (countryValue == "S")
             }

Then I need to return the obj object already with the new values for countryValue. What’s the easiest way to do it? I have a piece of code but it doesn’t work as it should.

SomeObject obj; // complex object that contains multiple levels, obj contains CountryUnit object


public class CountryUnit {

      private Set<Country> countries = new HashSet<>();

      // getters, setters
}

public class Country {

        private String countryValue;

        // getters, setters
}

My code:

List<String> CountryChecker = Arrays.asList("A", "B", "C");
List<String> allowedCountryList = Arrays.asList("L", "M", "N", "O", "P", "R", "S");

            obj.getSomeSet().stream()
                    .map(CountryUnit::getCountries)
                    .flatMap(Set::stream)
                    .filter((x) -> CountryChecker.contains(x.getCountryValue()))
                    .map(y -> {
                        Random r = new Random();
                        int rCountry = r.nextInt(allowedCountryList.size());
                        y.setCountryValue(allowedCountryList.get(rCountry));
                        return y;
                    });

Answer

You are almost there. Since you are already updating existing Country object, use a forEach rather than a map operator on the stream.

obj.getSomeSet().stream()
            .map(CountryUnit::getCountries)
            .flatMap(Set::stream)
            .filter((x) -> CountryChecker.contains(x.getCountryValue()))
            .forEach(y -> {
                Random r = new Random();
                int rCountry = r.nextInt(allowedCountryList.size());
                y.setCountryValue(allowedCountryList.get(rCountry));
            });

And you can combine the map and the flatMap calls as:

obj.getSomeSet().stream()
            .flatMap(countryUnit -> countryUnit.getCountries().stream())
            .filter(country -> CountryChecker.contains(country.getCountryValue()))
            .forEach(country -> {
                Random r = new Random();
                int rCountry = r.nextInt(allowedCountryList.size());
                country.setCountryValue(allowedCountryList.get(rCountry));
            });

If you follow Java’s naming convention, you should rename variable names to start with a lower-case (countryChecker or countriesToUpdate). It would be better if it is a Set because we call contains on it. With a set, that will be a constant time operation rather than a linear search.