我有以下两种与ArrayList get
方法相关的情况,一种是自定义类,另一种是String类:
1。下面是修改Custom class ArrayList元素的示例:
ArrayList<MyClass> mTmpArray1 = new ArrayList<MyClass>();
MyClass myObj1 = new MyClass(10);
mTmpArray1.add(myObj1);
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
MyClass myObj3 = mTmpArray1.get(0);
Log.d(TAG, "Int Value:"+myObj3.myInt); // Prints "20"
2。以下是修改String ArrayList元素的示例:
ArrayList<String> mTmpArray2 = new ArrayList<String>();
mTmpArray2.add("Test_10");
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
String myStr2 = mTmpArray2.get(0);
Log.d(TAG, "Str Value:"+myStr2); // Prints "Test_10"
因此,在MyClass ArrayList的情况下,当我调用get
并修改值时,我看到当我再次get
时,更改正在反映。
但是当我修改String ArrayList时,改变并没有反映出来。
两种情景中get
方法的不同之处是什么?
是在String的情况下,String类创建深层副本并返回新对象,并且在Custom类的情况下创建浅层副本?
在适用于“LinkedHashMap”,“HashMap”和“List”的第一个场景中?
答案 0 :(得分:3)
在这两种情况下你没有做同样的事情。
在此更新对象的状态,因此更改会影响列表中存储的对象:
myObj2.myInt = 20;
在这里,您要为局部变量分配一个新对象,因此列表不受影响:
myStr1 = "Test_20";
如果String
是可变的,您可以通过调用某个方法修改String,并且更改将反映在列表中存储的对象中:
myStr1.setSomething(...);
另一方面,如果在第一种情况下您将更改局部变量的值,则列表中存储的对象不会受到影响:
myObj2 = new MyClass (...);
答案 1 :(得分:2)
字符串是不可变的。您没有将新字符串插入数组列表。
当你执行String myStr2 = mTmpArray2.get(0);
时,即使你指向ArrayList中的引用,任何改变值的尝试都会(因为String immutability)会创建一个不会引用它的新String(myStr2) ArrayList了。
执行myStr1 = "xxx"
时,您实际上并没有更改ArrayList引用,而是要更改从ArrayList中获取的新(副本)(现在称为myStr1)并且它具有当地范围。
详细了解字符串:Immutability of Strings in Java
现在在第一个示例中,您指向一个可变对象(您的自定义类),因此您可以通过引用直接更改直接值。欢迎来到Java。 ;)
无关:此代码:MyClass myObj1 = new MyClass(10);
(可以说)被认为是错误的。最好使用更容易阅读的 factory 模式。换句话说,带参数的公共构造函数很难阅读(例如,当我读取代码时,我不知道我在构建什么)。
(或许)更好的方法是:MyClass myObj = MyClass.createInstanceWithCapacity(10); // i've invented the name because I don't know what your 10 is, but look at both, which one do you think is easier to understand upon first glance?
免责声明:以上不相关的评论是我个人的观点,并非所有开发者都会同意。 ;)
答案 2 :(得分:1)
字符串具有非常好的属性,称为“Immutablity”
这意味着当我们创建/时,String不能是可变的(已更改) 尝试引用旧字符串,创建一个新的实例字符串。而且任何 我们所做的更改将保存在新实例中,并且不会影响旧实例 串
例如,
String s = "Old String";
System.out.println("Old String : "+s); // output : Old String
String s2 = s;
s2 = s2.concat(" made New");
System.out.println("New String : "+s2); // output : Old String made New
System.out.println("Old String is not changed : "+s); // output : Old String
答案 3 :(得分:0)
这两者之间没有区别&#34;得到&#34;调用。不同之处在于ArrayList所持有的类型,以及您正在进行的引用&#34; get&#34;方法返回。
在您的第一个示例中,您执行此操作:
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
在这里,您在位置0的ArrayList中获取对MyClass实例的引用,并且您正在修改此实例中的字段。
在你的第二个例子中,你这样做:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
在这里,您将获得对数组列表中String实例的引用,然后您为myStr1提供对您创建的其他字符串的引用(&#34; Test_20&#34;) 。就像你在第一个例子的第二行写了myObj2 = new MyClass(20);
一样。
因此,简而言之,在第一个示例中,您可以通过从您抓取的引用中更改对象来访问对象中的字段。在第二个示例中,您只需将引用更改为指向不同的String。
我还应该提到,在Java中,字符串是不可变的,这意味着一旦创建它们就无法更改。
答案 4 :(得分:0)
String是一个不可变的类。像
这样的一行myStr1 = "Test_20";
不会更改指向的myStr1
字符串对象的值。而是创建一个新的String,并修改myStr1
以指向新的String。原始String保持不变,可以从ArrayList中检索。
您的MyClass
对象显然是可变的。只创建一个实例,并通过赋值更改其状态
myObj2.myInt = 20;
因此,当从ArrayList检索此对象时,会看到其新状态。
答案 5 :(得分:0)
您只是不要在第二个示例中更改列表。
在第一个例子中,您正在执行此操作:
但你的第二个例子完全不同:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
让我翻译一下:
因此,如果您正在修改列表中存储的对象的变量。在第二种情况下,您正在检索存储在列表中的对象 - 然后重新使用您存储检索到的对象的变量并将其用于新的 - 但这当然不会改变原始列表。
要修改类似字符串的类型列表,您需要使用set(x,“Test_20”)。