Почему можно изменить ArrayList пользовательских объектов в цикле for ... each - PullRequest
0 голосов
/ 05 сентября 2018

У нас есть простой пользовательский объект:

public class CustomObject {
    public CustomObject(int myIntNumber, String myString) {
    this.myIntNumber = myIntNumber ;
    this.myString = myString;    
    }

    private int myIntNumber;
    private String myString;

    public void setMyIntNumber(int myIntNumber) {
    this.myIntNumber = myIntNumber;
}

public int getMyIntNumber() {
    return myIntNumber;
}

public void setMyString(String myString) {
    this.myString = myString;
}

public String getMyString() {
    return myString;
}


    public String toString() {
        return "CustomObject [" + String.valueOf(myIntNumber)  + ", "+ myString+"]" ;  
    }
}

и мы пытаемся изменить ArrayList таких объектов с помощью цикла for ... each. Почему объекты в списке модифицируются, когда ArrayList объектов String или объектов Integer нельзя изменить таким способом?

Мой тестовый код:

import java.util.ArrayList;

public class TestTraveringListModification {

    public static void main(String[] args) {

    ArrayList<String> sList  = new ArrayList<String>();
    sList.add("String a");
    sList.add("String b");
    sList.add("C");
    sList.add("D");
    sList.add("String f");
    sList.add("String e");

    System.out.println("Before: "+sList);
    for (String s : sList) {
        s="asdf" ;

    }

    System.out.println("After: "+ sList);

    ArrayList<CustomObject> objL = new ArrayList<CustomObject> () ;
    objL.add(new CustomObject (1, "test") );
    objL.add(new CustomObject (2, "jim") );
    objL.add(new CustomObject (20, "dec") );
    objL.add(new CustomObject (60, "what") );
  System.out.println("before: "+ objL );
    for(CustomObject co : objL ){    
        co.setMyIntNumber(-1);
        co.setMyString("modified String");
    }
    System.out.println("after: "+objL);


    ArrayList<Integer> numList = new ArrayList<Integer>(); 

    numList.add(1);
    numList.add(3);
    numList.add(5);
    numList.add(67);
    numList.add(9598);

    System.out.println("before: "+ numList);

    for (Integer i : numList){
        i = 8; 
    }
    System.out.println("after: "+ numList);

}
}

Выполнение этого приведет к следующему выводу:

Before: [String a, String b, C, D, String f, String e]

After: [String a, String b, C, D, String f, String e]

before: [CustomObject [1, test], CustomObject [2, jim], CustomObject [20, dec], CustomObject [60, what]]

after: [CustomObject [-1, modified String], CustomObject [-1, modified String], CustomObject [-1, modified String], CustomObject [-1, modified String]]

before: [1, 3, 5, 67, 9598]
  • Элемент списка

    after: [1, 3, 5, 67, 9598]

Итак, почему я могу изменять objL, а не sList или numList?

Ответы [ 3 ]

0 голосов
/ 05 сентября 2018

Вы фактически не изменяете элементы из своих списков String и Integer, вы просто воздействуете на новые значения локальных переменных в ваших циклах, и это новое значение просто теряется на следующем шаге цикла.

На ваш пользовательский объект вы получаете ссылку на объект и вызываете метод, который модифицирует его. Вы можете сделать то же самое для String или Integer, если бы были такие методы. Но, к сожалению (или нет?), В классе String нет метода для изменения его значения.

0 голосов
/ 05 сентября 2018

Я думаю, это можно выразить так:

Список строк:

list[0] -> memory addr 001 -> "foo"
list[1] -> memory addr 002 -> "bar"
list[2] -> memory addr 003 -> "blah"

Теперь ваш for-each сделал переменную s для хранения ссылки 001:

s -> memory addr 001 -> "foo"

Когда вы делаете s="asdf":

s -> memory addr 00x -> "asdf"

Таким образом, строка с addr 001 не была изменена, вместо этого был создан новый строковый объект 00x. Теперь s содержит эту новую ссылку, однако старая ссылка 001 все еще удерживалась list[0], поэтому строки в списке не были изменены. Вы только что изменили ссылки на s. То же самое для Integer, i=8 заставит i содержать новую ссылку.

Однако для списка объектов все по-другому. Вы используете ту же ссылку на объект для выполнения setter(), поэтому вы изменили объект, список также был изменен.

0 голосов
/ 05 сентября 2018

Потому что переназначение и мутация - это две разные вещи.

s = "asdf" изменит то, на что ссылается s. Раньше он содержал ссылку на член sList, теперь он ссылается на "asdf". Изменение не имеет ничего общего с членом sList.

Аналогично i и numList, но не совсем точно так же. numList содержит Integer объекты, автоматически упакованные из 1, 3, 5 ... for назначат для целочисленных объектов значение i. Если затем изменить значение i на (автоматически) Integer(8), оно также не повлияет на numList any.

Однако с co и objL вы делаете совсем другое. Вместо переназначения co другому объекту (который не повлияет на объекты в objL), вы решили вызывать методы для co, которые затем изменили их состояние. Обратите внимание, что здесь вы не модифицируете objL, вы модифицируете содержащиеся в нем объекты.

Основное понимание состоит в том, что i, co и s не являются элементами соответствующих списков. Они содержат значения , которые могут быть элементами списков (но, опять же, в случае автоматической коробки это тоже не выполняется).

...