【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 
View Code

   上面的代码可以知道,存储Person这个对象时(存储的是地址),b和a其实是同一个地址,所以a指向的对象改变,会引起链表中的前两个结点(地址相同)改变,而如果要使存进链表的person存储的是a存储时的状态,只能新建一个对象c,令c的内容等于a,才在后面不会发生变化(因为该地址指向的内容没有再发生改变了)。

   关于String的讨论:其实String也是对象,存储的其实也是引用(地址),但为什么上面代码中before和after输出的内容都是“abc”呢?其实在aString="123";时,相当于aString=new String("123"),即aString指向了另一个对象,aString存储的地址变成了“123”的地址,但链表中存储的还是“abc”的地址,所以链表中的内容不变。

  更详细的传递讨论:值传递和引用传递讨论