相信很多人在初學java的時候,都會遇到一個同樣的問題,就是當我們認爲自己已經把一個list加入到List<List<T>> 中去了,爲什麼最後結果發現不對。如這個例子。
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 3; i++) {
list.add(i);
res.add(list);
}
return res;
假設,我們想得到的數據是{ {0}, {0, 1}, {0, 1, 2} } 這樣的結果,請問這個res是不是得到了這樣的結果?
如果不是,
請問,res.size()==?,然後res裏的數據是什麼樣的?
首先,這裏只通過ArrayList構造器創建了一個List的實例,也就是說整個過程中,只有一個真正意義上的List Object,但是res.size() == 3。
不僅如此,res == { {0, 1, 2}, {0, 1, 2}, {0, 1, 2} };
本人在剛開始學習的過程中,難以預計到這樣的結果。
其實原因也很簡單。在java中,有不同的數據類型。其中,原始數據類型之間是值傳遞,而其他的數據類型是引用傳遞。
而在java中,一個Object或者Array被創建時,將會在堆中分配內存,同時產生一個該Object或者Array的引用變量(reference)放在函數的棧內存中。
也就是說,list只是一個相對應類別的引用名稱,或者說是一個句柄(handle)。
當res.add(list)發生時,一個引用變量被傳入。在for循環中,被傳入了三次。即使在傳入的過程中,list所指向的Object所包含的內容不同,卻是同一個具體的實例。
最終res裏包含的就是三次在return位置時,list所指向的Object的值,也就是 {0, 1, 2}。
爲了避免這樣的結果導致實際工作中的錯誤,一般而言,
res.add(new ArrayList<Integer>(list));
在加入一個引用型變量時,產生一個新的Object來避免問題的出現。
List<List<Integer>> res = new ArrayList<List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 3; i++) {
list.add(i);
res.add(new ArrayList<Integer>(list));
}
return res;
// res == { { 0 }, { 0, 1 }, { 0, 1, 2} }