我们有一个简单的自定义对象:
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?
答案 0 :(得分:6)
因为重新分配和突变是两件事。
s = "asdf"
将更改s
所指的内容。它曾经包含对sList
成员的引用,现在它引用了"asdf"
。更改与sList
的成员无关。
类似于i
和numList
,尽管并不完全相同。 numList
包含Integer
个对象,它们从1
,3
,5
...自动装箱。for
会将Integer对象值分配给{{1 }}。然后,如果您将i
的值更改为(自动装箱)i
,则也不会影响Integer(8)
。
但是,使用numList
和co
,您所做的事情就完全不同了。与其将objL
重新分配给另一个对象(不会影响co
中的对象),而是选择在objL
上调用方法,然后碰巧改变了它们的状态。请注意,这里您不修改co
,而是在修改它包含的对象。
关键见解是objL
,i
和co
不是相应列表的元素。它们可能包含 作为列表元素的值(但在自动拆箱的情况下,同样不成立)。
答案 1 :(得分:1)
实际上,您不是在修改String和Integer列表中的项目,只是在循环中影响新值到局部变量,而这个新值只会在循环的下一步中丢失。
在自定义对象上,您获得对该对象的引用并调用对其进行修改的方法。如果有这样的方法,您可以在String或Integers上执行相同的操作。但是不幸的是(或者不是?),String类中没有方法可以修改其值。
答案 2 :(得分:1)
我认为可以这样写:
字符串列表:
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
持有的引用。与整数相同,i=8
将使i
拥有新的引用。
但是对于对象列表,它是不同的。您使用相同的对象引用执行setter()
,因此您更改了对象,列表也已更改。