Java List元素爲對象拷貝問題

java數據類型: 基本數據類型和引用數據類型

  • 基本數據類型: byte、short、int、long、float、double、char、boolean
  • 引用數據類型: 類、接口、數組

list本質上是數組,如果數組的元素類型是基本數據類型,則存儲的是值內容; 如果是對象,則數組的值以地址的形式進行存儲。

 

驗證

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PackageDTO implements Serializable {
    private Long pkgId;
    private String pkgCode;
    private String pkgName;

    @Override
    public String toString() {
        return String.format("id=%d, code=%s, name=%s",
                pkgId, pkgCode, pkgName);
    }
}
public class ListCopyApp {

    public static void main(String[] args) throws Exception {
        List<PackageDTO> packages = new ArrayList<>();
        PackageDTO package1 = new PackageDTO(1L, "A0001", "第一個押包");
        packages.add(package1);
        PackageDTO package2 = new PackageDTO(2L, "A0002", "第二個押包");
        packages.add(package2);

        System.out.println("打印原始數據");
        printList(packages);

        ArrayList<PackageDTO> copyByNewArrayList = Lists.newArrayList(packages);
        ArrayList<PackageDTO> copyByArrayList = new ArrayList<>(packages);
        ArrayList<PackageDTO> copyByLoop = new ArrayList<>(packages.size());
        packages.forEach(e -> copyByLoop.add(e));
        List<PackageDTO> copyBydeep = deepCopy(packages);

        System.out.println("進行集合內數據修改...\n\n");
        package1.setPkgId(-1L);
        package1.setPkgCode("B0001");
        package1.setPkgName("修改名稱啦");

        System.out.println("打印原始數據(修改後)");
        printList(packages);
        System.out.println("打印 copyByNewArrayList");
        printList(copyByNewArrayList);
        System.out.println("打印 copyByArrayList");
        printList(copyByArrayList);
        System.out.println("打印 copyByLoop");
        printList(copyByLoop);
        System.out.println("打印 copyBydeep");
        printList(copyBydeep);
    }


    public static <T> void printList(List<T> list) {
        list.forEach(System.out::println);
        System.out.println();
    }

    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(src);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (List<T>) ois.readObject();
    }
}

程序運行結果

打印原始數據
id=1, code=A0001, name=第一個押包
id=2, code=A0002, name=第二個押包

進行集合內數據修改...


打印原始數據(修改後)
id=-1, code=B0001, name=修改名稱啦
id=2, code=A0002, name=第二個押包

打印 copyByNewArrayList
id=-1, code=B0001, name=修改名稱啦
id=2, code=A0002, name=第二個押包

打印 copyByArrayList
id=-1, code=B0001, name=修改名稱啦
id=2, code=A0002, name=第二個押包

打印 copyByLoop
id=-1, code=B0001, name=修改名稱啦
id=2, code=A0002, name=第二個押包

打印 copyBydeep
id=1, code=A0001, name=第一個押包
id=2, code=A0002, name=第二個押包

 

總結

其實,上面這些不靠譜List深複製的做法在某些情況是可行的,這也是爲什麼有些人說這其中的一些做法是可以實現深複製的原因。哪些情況下是可行(本質上可能還是不靠譜)的呢?比如List這樣的情況。我上面使用的是List,它和List的區別就在於PackageDTO類和String類的區別,PackageDTO類提供了破壞數據的3個setter方法。因此,在淺複製的情況下,源數據被修改破壞之後,使用相同引用指向該數據的目標集合中的對應元素也就發生了相同的變化。

因此,在需求要求必須深複製的情況下,要是使用上面提到的方法,請確保List中的T類對象是不易被外部修改和破壞的。

 

Reference

一些不靠譜的java.util.List深複製方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章