2017-07-02 51 views
0

我遇到它簡化到像這樣的一個問題:爲什麼ArrayList副本的行爲與整數副本不一樣?

public static void main(String[] args) { 
    ArrayList<String> firstList = new ArrayList<>(); 
    firstList.add("Hello"); 
    firstList.add("World"); 
    firstList.add("This"); 
    firstList.add("Is"); 
    firstList.add("A"); 
    firstList.add("Test"); 

    System.out.println("Size of firstList: " + firstList.size()); 

    ArrayList<String> tempList = firstList; 
    System.out.println("Size of tempList: " + tempList.size()); 

    firstList.clear(); 
    System.out.println("Size of firstList: " + firstList.size()); // should be 0 
    System.out.println("Size of tempList: " + tempList.size()); 
} 

,輸出是:

Size of firstList: 6 
Size of tempList: 6 
Size of firstList: 0 
Size of tempList: 0 

我期望的tempList第二次大小輪是6而不是0

已經有一些與此效果有關的問題,如this oneanother one

從答案,我發現,這是因爲tempList是指同一基準firstList所以當firstList變化也是如此tempList(糾正我,如果我錯了這裏)。

因此,一個更好的解決方案,這將是這樣的:

ArrayList<String> tempList = new ArrayList<String>(firstList); 

如果有關引用的上述信息是真實的,那麼,爲什麼這樣的代碼:

public static void main(String[] args) { 
    int firstValue = 5; 

    System.out.println("firstValue: " + firstValue); 

    int tempValue = firstValue; 
    System.out.println("tempValue: " + firstValue); 

    firstValue = 3; 
    System.out.println("firstValue: " + firstValue); 
    System.out.println("tempValue: " + tempValue); 
} 

給這個輸出:

firstValue: 5 
tempValue: 5 
firstValue: 3 
tempValue: 5 

應該tempValue不是也是3第二次打印?

我覺得我誤解了引用是如何工作的,所以有人可以解釋爲什麼第一個示例中的臨時列表和原始列表一起受到影響,而臨時整數和原始整數給出與第二個示例中不同的結果?

回答

0

由於原始值沒有引用,並且包裝類型是不可變的(因此Integer的行爲方式相同)。數組作爲反例,是引用類型(甚至是基元數組)。所以,

int[] arr = { 1 }; 
int[] arrCopy = Arrays.copyOf(arr, arr.length); // <-- deep copy 
int[] b = arr; // <-- shallow copy 
b[0] = 2; // <-- same as arr[0] = 2 
System.out.printf("arr = %s, arrCopy = %s, b = %s%n", // 
     Arrays.toString(arr), Arrays.toString(arrCopy), // 
     Arrays.toString(b)); 

輸出

arr = [2], arrCopy = [1], b = [2] 

也參見,What is the difference between a deep copy and a shallow copy?

1

對於ArrayList的例子中,兩個變量同一對象別處在堆存儲器中,從而改變之一的內容參考影響另一個。

至於整數例子,當前的行爲是預期的,因爲它們是原始類型而不是引用類型。當涉及基本類型時,變量將存儲該值本身,而不是對內存中對象的引用。

2

Java有兩種變量類型:基元和引用。當一個變量分配給另一個,如在

ArrayList<String> tempList = firstList; 

要分配存儲在firstList是可變tempList的值的參考。重要的是要注意,您並未使用此作業創建新的ArrayList對象;你只是簡單地複製一個參考值。

當你與原語分配:

int tempValue = firstValue; 

你正在做同樣的事情,但有一個原始值。

一般來說,可以這樣想。變量不共享內存位置;每個變量都有自己的值。但是,如果該值是引用,則兩個引用變量可能包含相同的引用(就像兩個基本變量可能包含相同的基本值,例如5)。對於參考類型,如果您使用一個變量來更改所引用對象的內容,那麼您將在通過第二個變量訪問時看到該對象中的更改。在計算機科學文獻中,這種行爲被稱爲aliasing。對於原語,這種別名不會發生。

1

int tempValue是一個原始類型,這意味着它直接按值存儲。 ArrayList tempList不是原始類型,因此通過引用進行存儲。

您看到的與int發生的事情也會發生在所有其他Java靜態類型的基本變量中。同時,Java非基元變量在變化時會隨着變化引起的變化而變化。

(這會導致後續問題:?當您使用firstList = new ArrayList()代替firstList.clear()發生了什麼tempList是它的價值重新分配和突變,爲什麼之間的相同)

相關問題