为什么可以在 for...each 循环中修改自定义对象的 ArrayList
Why is it possible to modify an ArrayList of Custom Objects in a for...each loop
我们有一个简单的自定义对象:
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+"]" ;
}
}
并且,我们尝试使用 for...each 循环修改此类对象的 ArrayList。当不能以这种方式修改 String 对象或 Integer 对象的 ArrayList 时,为什么列表中的对象会被修改?
我的测试代码:
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?
因为重新分配和变异是两个不同的东西。
s = "asdf"
将改变 s
所指的内容。它曾经包含对 sList
成员的引用,现在它引用 "asdf"
。变更与sList
.
成员无关
与 i
和 numList
相似,但不完全相同。 numList
包含 Integer
个对象,从 1
、3
、5
自动装箱... for
会将 Integer 对象值分配给 i
.如果您随后将 i
的值更改为(自动装箱)Integer(8)
,它也不会影响 numList
。
然而,使用 co
和 objL
,你做的事情就完全不同了。您没有将 co
重新分配给另一个对象(这不会影响 objL
中的对象),而是选择调用 co
上的方法,然后碰巧改变了它们的状态。请注意,这里您不是修改objL
,您修改的是它包含的对象。
关键是 i
、co
和 s
不是相应列表的元素。它们包含值,可能是列表的元素(但同样在自动拆箱的情况下,这也不成立)。
您实际上并没有修改字符串和整数列表中的项目,您只是在影响循环中局部变量的新值,而这个新值只是在循环的下一步中丢失。
在您的自定义对象上,您可以获得对该对象的引用并调用修改它的方法。如果有这样的方法,您可以对 String 或 Integers 执行相同的操作。但不幸的是(或者不是?)String class 中没有方法来修改它的值。
我觉得可以这么说:
字符串列表:
list[0] -> memory addr 001 -> "foo"
list[1] -> memory addr 002 -> "bar"
list[2] -> memory addr 003 -> "blah"
现在你的 for-each 做了一个 var s
来保存引用 001
:
s -> memory addr 001 -> "foo"
当你做 s="asdf"
:
s -> memory addr 00x -> "asdf"
因此没有更改带有 addr 001
的字符串,而是创建了一个新的字符串对象 00x
。现在 s
包含这个新引用,但是旧引用 001
仍然由 list[0]
持有,因此列表中的字符串没有改变。您刚刚更改了 s
持有的引用。与整数相同,i=8
将使 i
保持新的引用。
但是对象列表就不一样了。您使用相同的对象引用来执行 setter()
因此您更改了对象,列表也已更改。
我们有一个简单的自定义对象:
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+"]" ;
}
}
并且,我们尝试使用 for...each 循环修改此类对象的 ArrayList。当不能以这种方式修改 String 对象或 Integer 对象的 ArrayList 时,为什么列表中的对象会被修改?
我的测试代码:
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?
因为重新分配和变异是两个不同的东西。
s = "asdf"
将改变 s
所指的内容。它曾经包含对 sList
成员的引用,现在它引用 "asdf"
。变更与sList
.
与 i
和 numList
相似,但不完全相同。 numList
包含 Integer
个对象,从 1
、3
、5
自动装箱... for
会将 Integer 对象值分配给 i
.如果您随后将 i
的值更改为(自动装箱)Integer(8)
,它也不会影响 numList
。
然而,使用 co
和 objL
,你做的事情就完全不同了。您没有将 co
重新分配给另一个对象(这不会影响 objL
中的对象),而是选择调用 co
上的方法,然后碰巧改变了它们的状态。请注意,这里您不是修改objL
,您修改的是它包含的对象。
关键是 i
、co
和 s
不是相应列表的元素。它们包含值,可能是列表的元素(但同样在自动拆箱的情况下,这也不成立)。
您实际上并没有修改字符串和整数列表中的项目,您只是在影响循环中局部变量的新值,而这个新值只是在循环的下一步中丢失。
在您的自定义对象上,您可以获得对该对象的引用并调用修改它的方法。如果有这样的方法,您可以对 String 或 Integers 执行相同的操作。但不幸的是(或者不是?)String class 中没有方法来修改它的值。
我觉得可以这么说:
字符串列表:
list[0] -> memory addr 001 -> "foo"
list[1] -> memory addr 002 -> "bar"
list[2] -> memory addr 003 -> "blah"
现在你的 for-each 做了一个 var s
来保存引用 001
:
s -> memory addr 001 -> "foo"
当你做 s="asdf"
:
s -> memory addr 00x -> "asdf"
因此没有更改带有 addr 001
的字符串,而是创建了一个新的字符串对象 00x
。现在 s
包含这个新引用,但是旧引用 001
仍然由 list[0]
持有,因此列表中的字符串没有改变。您刚刚更改了 s
持有的引用。与整数相同,i=8
将使 i
保持新的引用。
但是对象列表就不一样了。您使用相同的对象引用来执行 setter()
因此您更改了对象,列表也已更改。