Java数组的复制有很多方法,但绝大多数是浅复制,下面寻找探索Java数组的复制方法,并验证其是深复制还是浅复制。
这是下面要频繁使用的一个JavaBean。
package com.bijian.test; import java.io.Serializable; class Person implements Serializable { private int age; private String name; public Person(){}; public Person(int age,String name){ this.age=age; this.name=name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString(){ return this.name+"-->"+this.age; } }
后台打印List集合、数组的静态方法类。
package com.bijian.test; import java.util.List; public class PrintUtils { public static <T> void printList(List<T> list){ System.out.println("---begin---"); for(T t : list){ System.out.println(t); } System.out.println("---end---"); } public static <T> void printArray(T[] array){ System.out.println("---begin---"); for(T t : array){ System.out.println(t); } System.out.println("---end---"); } }
这是数据源集合,下面将通过各种方法企图来深复制该List集合中的元素。
public static List<Person> init() { List<Person> srcList=new ArrayList<Person>(); Person p1=new Person(20,"123"); Person p2=new Person(21,"ABC"); Person p3=new Person(22,"abc"); srcList.add(p1); srcList.add(p2); srcList.add(p3); return srcList; }
一.遍历循环复制
/** * 遍历循环复制 * @param srcList */ public static void test01(List<Person> srcList) { List<Person> destList=new ArrayList<Person>(srcList.size()); for(Person p : srcList){ destList.add(p); } PrintUtils.printList(destList); srcList.get(0).setAge(100); PrintUtils.printList(destList); }
上面的代码在add时候,并没有new Person()操作。因此,在srcList.get(0).setAge(100);破坏源数据时,目标集合destList中元素的输出同样受到了影响,原因是浅复制造成的。
运行结果:
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end---
二.使用List实现类的构造方法
/** * 使用List实现类的构造方法 * @param srcList */ public static void test02(List<Person> srcList) { List<Person> destList=new ArrayList<Person>(srcList); PrintUtils.printList(destList); srcList.get(0).setAge(100); PrintUtils.printList(destList); }
通过ArrayList的构造方法来复制集合内容,同样是浅复制,在修改了源数据集合后,目标数据集合对应内容也发生了改变。
运行结果:
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end---
三.使用list.addAll()方法
/** * 使用list.addAll()方法 * @param srcList */ public static void test03(List<Person> srcList) { List<Person> destList=new ArrayList<Person>(srcList); PrintUtils.printList(destList); srcList.get(0).setAge(100); PrintUtils.printList(destList); }
java.util.list.addAll()方法同样是浅复制,运行结果如下:
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end---
四.使用System.arraycopy()方法
public static void test04(List<Person> srcList) { Person[] srcPersons=srcList.toArray(new Person[0]); Person[] destPersons=new Person[srcPersons.length]; System.arraycopy(srcPersons, 0, destPersons, 0, srcPersons.length); //destPersons=srcPersons.clone(); PrintUtils.printArray(destPersons); srcPersons[0].setAge(100); PrintUtils.printArray(destPersons); List<Person> destList=Arrays.asList(destPersons); PrintUtils.printList(destList); }
这种方式虽然比较变态,但是起码证明了System.arraycopy()方法和clone()是不能对List集合进行深复制的。运行结果如下:
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end---
五.java.util.Collections.copy()方法
/** * 注意: * destList的初始化如是填数字,表示的是这个List的容纳能力,并不是说des1中就有了几个元素。 * 查看api才知 道,它的capacity(容纳能力大小)可以指定(最好指定)。而初始化时size的大小永远默认为0,只有在进行add和remove等相关操作 时,size的大小才变化。 * 然而进行copy()时候,首先做的是将desc1的size和src1的size大小进行比较,只有当desc1的 size 大于或者等于src1的size时才进行拷贝,否则抛出IndexOutOfBoundsException异常。 * * 因此如下实例都是通过Arrays.asList(new Person[srcList.size()])或CollectionUtils.addAll(destList, new Person[srcList.size()]);来进行destList的初始化的 * * @param srcList */ public static void test06(List<Person> srcList) { // List<Person> destList=new ArrayList<Person>(Arrays.asList(new Person[srcList.size()])); // Collections.copy(destList,srcList); // PrintUtils.printList(destList); // srcList.get(0).setAge(100); // PrintUtils.printList(destList); List<Person> destList=new ArrayList<Person>(); CollectionUtils.addAll(destList, new Person[srcList.size()]); Collections.copy(destList, srcList); PrintUtils.printList(destList); srcList.get(0).setAge(100); PrintUtils.printList(destList); }
运行结果如下,还是浅复制。
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->100 ABC-->21 abc-->22 ---end---
六.使用序列化方法(相对靠谱的方法)
public static void test05(List<Person> srcList) { try { List<Person> destList = deepCopy(srcList); PrintUtils.printList(destList); srcList.get(0).setAge(100); PrintUtils.printList(destList); } catch (Exception e) { e.printStackTrace(); } } public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(src); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); @SuppressWarnings("unchecked") List<T> dest = (List<T>) in.readObject(); return dest; }
这是比较靠谱的做法,听说是国外某位程序大师提出来的。实际运行的结果也同样是正确的。运行结果如下:
---begin--- 123-->20 ABC-->21 abc-->22 ---end--- ---begin--- 123-->20 ABC-->21 abc-->22 ---end---
其实,上面这些不靠谱List深复制的做法在某些情况是可行的,这也是为什么有些人说这其中的一些做法是可以实现深复制的原因。哪些情况下是可行(本质上可能还是不靠谱)的呢?比如List<String>这样的情况。我上面使用的是List<Person>,它和List<String>的区别就在于Person类和String类的区别,Person类提供了破坏数据的2个setter方法。因此,在浅复制的情况下,源数据被修改破坏之后,使用相同引用指向该数据的目标集合中的对应元素也就发生了相同的变化。
因此,在需求要求必须深复制的情况下,要是使用上面提到的方法,请确保List<T>中的T类对象是不易被外部修改和破坏的。
相关推荐
总的来说,Java中的对象复制可以通过多种方式实现,包括基本的复制方法和利用各种工具库,特别是通过注解可以实现更加灵活和定制化的复制策略。对于模型和视图对象的复制,这可以大大提高开发效率,减少错误,并使...
本文将深入探讨Java中的浅复制和深复制,并以Android为背景,结合具体的实例讲解如何实现Java泛型深拷贝。 首先,我们要理解浅复制和深复制的概念。浅复制(Shallow Copy)是指创建一个新的对象,然后将原对象引用...
要进行深度拷贝(即完全复制对象的所有属性),需实现`Cloneable`接口并重写`clone()`方法。 2. **Arrays类的深拷贝** Java的`Arrays`类提供了`copyOf()`和`copyOfRange()`方法,用于数组的数据复制。这两个方法...
《Java深度历险》这本书是Java开发者们探索编程世界的宝贵指南,它涵盖了Java语言的核心概念、高级特性以及实际开发中的重要应用。通过本书,读者可以深入理解Java编程的精髓,从而提升自己的技能水平。 首先,Java...
beancopy ###java bean深度克隆的简单实现 Bean中必须有对应的getter, setter方法. 可以复制包含 "8种基本类型, String, java.util.Date, enum, Set, List 以及多层嵌套" 的Bean.
2. **集合框架**:Java集合框架包括List、Set、Queue、Map等接口及其实现,如ArrayList、HashSet、LinkedList、HashMap等。这部分可能会讨论它们各自的特性和适用场景,以及如何使用泛型和并发容器。 3. **多线程**...
因为SUN的Stack不是严格意义上的stack,因此需要重写下,是这样的
7. **算法实现**:源码包中的200多个程序可能涵盖了各种算法的Java实现,例如排序算法(如冒泡排序、快速排序、归并排序)、搜索算法(如二分查找、深度优先搜索、广度优先搜索)、图论问题、动态规划等。...
例如,使用`Files.walk()`方法可以方便地深度遍历目录及其所有子目录: ```java Path startPath = Paths.get("path/to/directory"); try (Stream<Path> stream = Files.walk(startPath)) { stream.forEach(path ->...
《Java集合框架深度解析》 Java集合框架是Java编程语言中的一个重要组成部分,它为数据存储提供了丰富的类和接口。在本文中,我们将深入探讨Java集合框架的核心概念、设计原理以及实际应用。 首先,我们来理解Java...
`java.nio.file.Files.copy()` 方法可以用来复制文件,它支持复制文件属性,例如访问权限、创建时间等。对于目录,我们需要递归地遍历子目录并逐个复制。 4. **错误处理**:在执行过程中可能会遇到各种错误,比如...
这个"java 递归拷贝文件显示进度demo"是一个实用的示例,它不仅实现了从一个源目录到目标目录的深度拷贝,而且还提供了进度显示功能,这对于用户来说是一个很好的交互体验。以下是对这个Demo涉及知识点的详细解释: ...
此外,为了实现文件的遍历和查找,开发者可能使用了`java.io.File`的`list()`或`listFiles()`方法,这些方法返回指定目录下的所有文件和子目录。结合递归,可以实现深度优先或广度优先遍历整个文件系统。 在控制台...
Java程序设计是计算机科学中的重要课程,尤其在清华大学这样的顶级学府中,其教学质量与深度备受业界关注。郑莉老师是这门课程的主讲教师,她以其深厚的学术背景和丰富的教学经验,引领学生们深入理解Java语言的核心...
美团系统交易面试资料整理涉及到的Java知识点涵盖了Java基础、JVM原理、集合...面试中的Java问题往往考察应聘者对于Java语言和其运行机制的理解深度和应用能力,因此熟练掌握这些知识点将大大提升面试成功的可能性。
Java核心API的丰富性和深度,反映了Java作为一门强大而灵活的语言的本质。通过对这些包的理解和掌握,开发者可以构建出功能丰富、性能高效的应用程序,无论是桌面应用、Web服务还是移动应用,Java都能提供坚实的支持...
为了得到深拷贝,即完全独立的副本,你需要手动实现深度复制逻辑,例如,遍历引用类型字段并逐个复制每个元素。 总之,`clone()`方法在Java中提供了对对象复制的基本支持,尤其适用于需要创建对象副本的情况。然而...
《CoreJava12示例代码》是一份针对Java编程语言深度学习的重要资源,主要涵盖了CoreJava12版本中的核心概念和特性。这份压缩包文件包含了丰富的示例代码,旨在帮助开发者深入理解Java语言的各个方面,提升编程技能。...