Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java stream map modifies custom class object not built in classes

Tags:

class Employee {

    public String name;
    public Integer age;
    public Employee(String n, int age) {
        this.name = n;
        this.age = age;
    }
    public String toString() {
        return this.name+":"+this.age;
    }
}

Inside Main:

ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee("NameA", 10));
list.add(new Employee("NameB", 25));
list.add(new Employee("NameC", 30));
list.add(new Employee("NameD", 45));
list.add(new Employee("NameE", 50));

System.out.println(list);//[NameA:10, NameB:25, NameC:30, NameD:45, NameE:50]

list.stream().filter(e->e.age%10==0).map(e->e.name+="CHANGE").collect(Collectors.toList());

System.out.println(list); //[NameACHANGE:10, NameB:25, NameCCHANGE:30, NameD:45, NameECHANGE:50]


ArrayList<String> strList = new ArrayList<>();
strList.add("1");
strList.add("2");
strList.add("3");
strList.add("4");
strList.add("5");

System.out.println(strList);//[1, 2, 3, 4, 5]

List<String> updatedStrList = strList.stream().map(s->s+="CHANGE").collect(Collectors.toList());

System.out.println(updatedStrList);//[1CHANGE, 2CHANGE, 3CHANGE, 4CHANGE, 5CHANGE]

System.out.println(strList);//[1, 2, 3, 4, 5]

What's the reason for this behaviour? When the Employee object's value is being changed the value gets updated in the original ArrayList but when ArrayList of String was changed the values did not reflect in the original ArrayList.

like image 592
Gagandeep Singh Avatar asked Sep 27 '18 06:09

Gagandeep Singh


2 Answers

There's a different between e.name+="CHANGE" and s->s+="CHANGE". Both of them create a new String instance, but while the first assigns that new String to an instance variable of an instance of the Employee class (and therefore mutates that instance), the second assigns it to a local String variable.

Therefore e.name+="CHANGE" changes the corresponding instance of the original List and s->s+="CHANGE" doesn't.

like image 57
Eran Avatar answered Nov 02 '22 06:11

Eran


The problem has nothing to do with streams.

In this lambda expression: s -> s += "CHANGE", you're just reassigning the local variable s. s += "CHANGE" also returns the result of the concatenation, and that explains why the stream shows the mapped value. This reassignment is only effective in the local scope of the lambda expression.

But e.name+="CHANGE" updates the field of the Employee object e. This updates the original object itself.

You should collect mapped values instead of updating original stream elements. Incidentally, updating original stream elements is not even an option in your case because you can't modify String objects.

like image 40
ernest_k Avatar answered Nov 02 '22 05:11

ernest_k