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深复制方法

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