我需要知道为什么会发生以下情况,在这段代码中(最后两个块)我希望完全相同的输出但是本地对象(仅仅引用列表中的那些,对吧?)是旧的列表更新时的状态。我有一个错误,因为我的游戏代码中有类似的复制过程(基于tile,对象交换位置,所以我想为什么不只是交换他们的引用...)
示例:
package game;
import java.util.ArrayList;
public class Tester {
private String s;
private Foo foo;
public Tester(String s, String f) {
this.s = s;
this.foo = new Foo(f);
}
class Foo {
private String f;
public Foo(String f) {
this.f = f;
}
}
@Override
public String toString() {
return foo.f + ":" + s;
}
public void swap(Tester other) {
String tempS = this.s;
Foo tempFoo = this.foo;
this.s = other.s;
this.foo = other.foo;
other.s = tempS;
other.foo = tempFoo;
}
public static void main(String[] args) {
ArrayList<Tester> test = new ArrayList<Tester>();
test.add(new Tester("First", "1"));
test.add(new Tester("Second", "2"));
System.out.println("After initializing list");
for (Tester t : test) {
System.out.println(t);
}
Tester first = test.get(0);
Tester second = test.get(1);
Tester tmp = first;
first = second;
second = tmp;
System.out.println("\nAfter temps");
System.out.println(first);
System.out.println(second);
System.out.println("\nList changed after temps?");
for (Tester t : test) {
System.out.println(t);
}
System.out.println("\nAfter swap");
first.swap(second);
System.out.println(first);
System.out.println(second);
System.out.println("\nList after swap");
for (Tester t : test) {
System.out.println(t);
}
}
}
输出:
After initializing list
1:First
2:Second
After temps
2:Second
1:First
List changed after temps?
1:First
2:Second
After swap
1:First
2:Second
List after swap
2:Second
1:First
我觉得我有点不清楚,我总是打印出第一个然后第二个(对象)所以“在交换后”应该看起来像“交换后的列表”,列表在交换本地对象后发生了变化,本地对象(再次,仅仅引用列表中的对象?)没有。
回答中的评论:当我的困惑被清除并且我的问题解决了之后,我问是否有可能将列表的实际引用提供给对象,以便在我指出它时对于其他东西,列表也会自动指向其他东西。在下面的示例中,Foo a
首先指向具有属性Foo
的对象A
,但此引用来自list.get()
。现在,如果我将其指向new Turtles()
,则列表仍会指向Foo with A
。
我希望有一个“硬”参考,所以当我指向海龟时,arraylist指向乌龟。
这会使交换变得更容易,但我还不确定如何实现可以执行此操作的列表(如果可能的话),这将非常适合我的游戏的特定需求。
答案 0 :(得分:3)
使用对象时需要注意的是保存对象的变量实际上并不代表对象。变量的值是内存中指向对象所在位置的指针。
某种类型的对象的ArrayList,通过扩展,实际上不包含对象。它包含对象引用或指向内存中对象位置的指针。
当你说:
Object a = new Object();
Object b = new Object();
a = b;
您将a
设置为指向 b
的值;换句话说,由于a
和b
都指向内存中的同一个对象,因此更改另一个会改变另一个对象。
现在回答你的问题,记住这一点。如果从ArrayList中提取两个对象引用,然后交换它们,则已将引用交换为对象;但是,引用没有更改ArrayList中的位置。这解释了前三个输出。
但是,在进行内部交换后,您已经更改了存储在每个对象中的值。这意味着,尽管指向第一个和第二个对象的指针在内存中仍然相同,但它们的内部信息已被交换。
这解释了最后两个输出。
(注意,这也是您必须使用System.arraycopy
而不是将int[] a
设置为int[] b
的原因;如果您要执行后者,则更改a会更改b。 )
答案 1 :(得分:1)
实际上你的交换方法不交换对象本身,只交换它们包含的值,字符串s
按值交换,对象foo
通过引用交换。
当你使用get(index)
从列表中获取一个元素时,除了列表所持有的内部引用之外,你实际上得到了对该对象的引用,所以你有两个引用指向同一个对象。 / p>
要真正交换列表的元素,你应该使用List的set方法:
Tester tmp = test.get(index);
test.set(index, test.get(index+1));
test.set(index+1, tmp);
这样,元素就会在列表中进行交换,因此迭代它会打印出您期望的结果。你拥有的swap
方法将会过时。