2013-04-07 46 views
0

我需要知道爲什麼會發生以下情況,在此代碼中(最後兩個塊)我期望完全相同的輸出,但本地對象(僅僅是引用列表中的對象,對嗎?)在列表更新時處於舊狀態。我有一個錯誤,因爲在我的gamecode類似的複製過程(tilebased,對象交換位置,所以我想,爲什麼不只是交換他們引用...)引用和複製對象,意外的行爲

例子:

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首先指向具有屬性A的對象Foo,但是此參考從list.get()獲得。現在,如果我將它指向new Turtles(),則列表仍將指向Foo with Aenter image description here

我一直希望有一個「硬」的參考,所以當我指向海龜時,數組列表指向海龜。 enter image description here

這樣做會使交換更容易,但我還不確定如何實現可以做到這一點的列表(如果可能的話),這將非常適合我的遊戲的特定需求。

+0

對於新用戶,您的代碼清爽整潔。謝謝! – Zyerah 2013-04-07 20:08:57

回答

3

使用對象時需要注意的一點是,保存對象的變量實際上並不代表對象。變量的值是內存中指向對象所在位置的指針。

某種類型的對象的ArrayList(擴展名)實際上並不包含對象。它保存對象引用或指向內存中對象位置的指針。

當你說:

Object a = new Object(); 
Object b = new Object(); 
a = b; 

你設置a指針的值來b;換句話說,因爲ab都指向內存中的同一個對象,所以更改一個會改變另一個。

現在回答你的問題,考慮到這一點。如果你從ArrayList中取出兩個對象引用,然後交換它們,你已經將引用交換到了對象中;但是,引用沒有改變ArrayList中的位置。這解釋了前三個輸出。

然而,一旦你做了內部交換,你已經改變了存儲在每個對象中的值。這意味着,雖然指向第一個和第二個對象的指針在內存中仍然是相同的,但它們的內部信息已被交換。

這解釋了最後兩個輸出。

(注意,這也就是爲什麼你必須使用System.arraycopy,而不是設置int[] a等於int[] b;如果你做的是後者,那麼改變將改變B)

+0

更準確地說,變量的*值*是指針/參考。 (變量本身仍然是內存中的另一個插槽,但無論如何,您無法真正處理變量作爲頭等實體。) – millimoose 2013-04-07 20:19:10

+0

啊,這一切都很有意義,這要歸功於您和A4L。所以,當你使用.get()時,你得到了對象的引用,但改變引用並不意味着該列表也會改變它對同一對象的引用,我可以看到爲什麼應該避免這種情況,因爲你不想改變列表中的內部順序是偶然的,但出於好奇,你如何從列表中獲得參考? – arynaq 2013-04-07 20:40:44

+0

@ user948652我不完全確定你在問什麼,但是如果我明白了,那麼只需使用'arraylist.get(index)'將返回引用。嘗試打印一個對象,你會看到一串數字 - 這就是內存地址。 – Zyerah 2013-04-07 21:00:30

1

其實您的交換方法不交換對象本身,但只有它們包含的值,字符串s被交換值,對象foo交換引用。

當您使用get(index)從列表中獲取元素時,除了該列表所持有的內部引用外,您實際上還獲得了該對象的引用,因此您在那裏有兩個指向同一對象的引用。

真正交換名單的元素,你應該使用名單的set方法有類似的東西:

Tester tmp = test.get(index); 
test.set(index, test.get(index+1)); 
test.set(index+1, tmp); 

這樣的元素在列表中,以便遍歷它將打印結果swaped你的期望。您擁有的swap方法將會過時。

+0

是的,但我想避免這種情況(預優化,我知道),因爲我認爲沒有理由覆蓋兩個元素,只需通過交換引用進行交換即可。目前正在閱讀Telthiens的答案,找出爲什麼輸出是正確的。 – arynaq 2013-04-07 20:32:54

+0

我其實也回答了這個問題:'所以你有兩個指向同一個對象的引用.'' get'調用之後交換的那些不會影響列表中的那些。 – A4L 2013-04-07 20:43:01