開門見山的說,List的復制其實是很常見的,List其本質就是數組,而其存儲的形式是地址
如圖所示,將List A列表復制時,其實相當於A的內容復制給了B,java中相同內容的數組指向同一地址,即進行淺拷貝后A與B指向同一地址。
造成的后果就是,改變B的同時也會改變A,因為改變B就是改變B所指向地址的內容,由於A也指向同一地址,所以A與B一起改變。
這也就是List的淺拷貝,其常見的實現方式有如下幾種:
淺拷貝
1、遍歷循環復制
List<Person> destList=new ArrayList<Person>(srcList.size()); for(Person p : srcList){ destList.add(p); }
2、使用List實現類的構造方法
List<Person> destList=new ArrayList<Person>(srcList);
3、使用list.addAll()方法
List<Person> destList=new ArrayList<Person>(); destList.addAll(srcList);
4、使用System.arraycopy()方法
Person[] srcPersons=srcList.toArray(new Person[0]); Person[] destPersons=new Person[srcPersons.length]; System.arraycopy(srcPersons, 0, destPersons, 0, srcPersons.length);
測試及結果
printList(destList); //打印未改變B之前的A srcList.get(0).setAge(100);//改變B printList(destList); //打印改變B后的A //打印結果 123-->20 ABC-->21 abc-->22 123-->100 ABC-->21 abc-->22
List 深拷貝
如圖,深拷貝就是將A復制給B的同時,給B創建新的地址,再將地址A的內容傳遞到地址B。ListA與ListB內容一致,但是由於所指向的地址不同,所以改變相互不受影響。
import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class CopyTest { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } //list深度拷貝 List<Integer> newList = new ArrayList<>(); CollectionUtils.addAll(newList, new Object[list.size()]); Collections.copy(newList, list); newList.set(0, 10); System.out.println("原list值:" + list); System.out.println("新list值:" + newList); } }
測試結果
原list值:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 新list值:[10, 1, 2, 3, 4, 5, 6, 7, 8, 9]
小結
Java對對象和基本的數據類型的處理是不一樣的。在Java中用對象的作為入口參數的傳遞則缺省為”引用傳遞”,也就是說僅僅傳遞了對象的一個”引用”,這個”引用”的概念同C語言中的指針引用是一樣的。當函數體內部對輸入變量改變時,實質上就是在對這個對象的直接操作。 除了在函數傳值的時候是”引用傳遞”,在任何用”=”向對象變量賦值的時候都是”引用傳遞”。
在淺復制的情況下,源數據被修改破壞之后,使用相同引用指向該數據的目標集合中的對應元素也就發生了相同的變化。因此,在需求要求必須深復制的情況下,要是使用上面提到的方法,請確保List中的T類對象是不易被外部修改和破壞的。