【Java】链表中存储对象的问题
更新:
在一次搜索“变量声明在循环体内还是循环体外”问题时,碰见了一个这样的代码,与本文类似,代码如下:
Document [] old ......//这是数据源 EntityDocument[] newArray = new EntityDocument[old.length];//自定义的类,为了把Document里数据保留下来避免Document被关联对象关闭而导致无法取出数据。 EntityDocument d = new EntityDocument(); for(int i=0;i<old.length;i++){ d.content = old[i].getContent(); d.key = old[i].getKey(); d...................... newArray[i] = d;//如此对象重用..... }
上面的代码最终结果会导致newArray数组中的每个元素都等于最后一个元素,原因就是每次向newArray中存储对象时,没有新建一个对象进行储存,从而导致错误。
上面代码只需要将第3行的 EntityDocument d = new EntityDocument(); 放入循环体内即可实现正常功能。
这种错误隐藏得比较深,所以要好好记住。
原文
在刷《剑指OFFER》的时候,自己犯了一个错误,发现:在链表中存储一个对象时,如果该对象是不断变化的,则应该创建一个新的对象复制该对象的内容(而不是指向同一个对象),将这个新的对象存储到链表中。如果直接存储该对象的话,链表中的对象也会不断变化。基本数据类型和String则没有这种问题。
其实这归根结底是一个传值和传引用的问题:
1.如果存储到链表中的是对象,则存储的是引用(地址),所以该地址上的内容变化时,会引起对象的变化。
2.如果存到链表中的是基本数据类型或者String,存储的就是该数值,不会再发生变化了。(其实String是对象,存储的是引用,后面讨论)。
举个例子:
import java.util.ArrayList; public class Test { public static class Person { int age=1; } public static void main(String[] args) { //=======ArrayList存储String或者基础数据类型========= ArrayList<String> list = new ArrayList<>(); String aString="abc"; list.add(aString); System.out.println("before:"+list.toString()); aString="123"; System.out.println("after:"+list.toString()); //========ArrayList存储对象======= ArrayList<Person> pList = new ArrayList<>(); Person a = new Person(); Person b = new Person(); b = a; Person c = new Person(); c.age=a.age; pList.add(a); pList.add(b); pList.add(c); System.out.print("before:"); for (Person person : pList) { System.out.print(person.age+" "); //a,b,c的age此时都是1 } System.out.println(); a.age=2; System.out.print("after:"); for (Person person : pList) { System.out.print(person.age+" "); //输出:2,2,1 } //关键原因:b是和a指向同一个对象,c不是同一个对象 } }
before:[abc] after:[abc] before:1 1 1 after:2 2 1
上面的代码可以知道,存储Person这个对象时(存储的是地址),b和a其实是同一个地址,所以a指向的对象改变,会引起链表中的前两个结点(地址相同)改变,而如果要使存进链表的person存储的是a存储时的状态,只能新建一个对象c,令c的内容等于a,才在后面不会发生变化(因为该地址指向的内容没有再发生改变了)。
关于String的讨论:其实String也是对象,存储的其实也是引用(地址),但为什么上面代码中before和after输出的内容都是“abc”呢?其实在aString="123";时,相当于aString=new String("123"),即aString指向了另一个对象,aString存储的地址变成了“123”的地址,但链表中存储的还是“abc”的地址,所以链表中的内容不变。
更详细的传递讨论:值传递和引用传递讨论