所有的测试代码在最下方。
1. 实现Cloneable接口
实现cloneable
接口,重写clone类。
直接使用object的clone方法,还是浅拷贝,需要自己重写clone方法,注意拷贝的对象中还包含其他对象的话,包含的对象也需要重写clone方法。
这种方法效率最高,但是不好维护
/**
* @Description:
* @Author: zhaoyuan
* @Date: 2021/4/14 0014
* @Time 14:45
*/
public class Parent implements Cloneable{
Integer id;
String name;
Child child;
List<List<Child>> childrenList;
List<Child> children;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
public List<List<Child>> getChildrenList() {
return childrenList;
}
public void setChildrenList(List<List<Child>> childrenList) {
this.childrenList = childrenList;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
@Override
protected Parent clone() throws CloneNotSupportedException {
Parent cloneParent = (Parent)super.clone();
if (Objects.nonNull(this.child)) {
cloneParent.setChild(this.child.clone());
}
if (!CollectionUtils.isEmpty(this.children)) {
List<Child> cloneChildren = new ArrayList<>();
for (Child child : this.children) {
cloneChildren.add(child.clone());
}
cloneParent.setChildren(cloneChildren);
}
if (!CollectionUtils.isEmpty(this.childrenList)) {
List<List<Child>> cloneChildrenList = new ArrayList<>();
for (List<Child> childList : this.childrenList) {
List<Child> cloneChild = new ArrayList<>();
for (Child child : childList) {
cloneChild.add(child.clone());
}
cloneChildrenList.add(cloneChild);
}
cloneParent.setChildrenList(cloneChildrenList);
}
return cloneParent;
}
}
/**
* @Description:
* @Author: zhaoyuan
* @Date: 2021/4/14 0014
* @Time 14:48
*/
public class Child implements Cloneable{
Integer id;
String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Child clone() throws CloneNotSupportedException {
return (Child)super.clone();
}
}
2. BeanUtils 是浅clone
3. 使用Orika是深clone,但是多层嵌套数组,clone有问题,谨慎使用
4. 序列化Clone,深clone,但是效率不高
5. 转json完成clone,深clone
我这里使用的hutool的json工具类转的,效率还没有序列化的方式高,但是其他人测试,Gson转比序列化效率高,这里不再测试,都没有自己重写clone效率高
6. mapStrcuct
还有使用mapStruct实现clone的,这里不过多解释,对象中有list存储对象的,也不建议使用,下面是mapStrcut生成的代码,list类型的,它是通过new ArrayList()
的方式,是浅拷贝
7. 所有测试代码
package com.example.demo.domain.deepClone;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.demo.util.OrikaMapperUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StopWatch;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:
* @Author: zhaoyuan
* @Date: 2021/4/14 0014
* @Time 15:00
*/
public class TestDeepClone {
public Parent parent;
public StopWatch watch;
@Before
public void before() {
parent = new Parent();
parent.setId(1);
parent.setName("parentName1");
parent.setChild(new Child(1,"childName1"));
Child child2 = new Child(2,"childName2");
Child child3 = new Child(2,"childName3");
Child child4 = new Child(2,"childName4");
List<Child> children = new ArrayList<>();
children.add(child2);
children.add(child3);
children.add(child4);
parent.setChildren(children);
List<List<Child>> childrenList = new ArrayList<>();
Child child5 = new Child(2,"childName5");
Child child6 = new Child(2,"childName6");
Child child7 = new Child(2,"childName7");
List<Child> children2 = new ArrayList<>();
List<Child> children3 = new ArrayList<>();
children2.add(child5);
children2.add(child6);
children3.add(child7);
childrenList.add(children2);
childrenList.add(children3);
parent.setChildrenList(childrenList);
watch = new StopWatch("clone");
}
@Test
public void testClone() throws CloneNotSupportedException {
watch.start();
Parent clone = parent.clone();
watch.stop();
System.out.println(watch.prettyPrint());
System.out.println(parent);
System.out.println(clone);
Assert.assertNotEquals(clone,parent);
Assert.assertNotEquals(clone.getChild(),parent.getChild());
Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
}
@Test
public void testBeanUtilCopy(){
watch.start();
Parent clone = new Parent();
BeanUtils.copyProperties(parent,clone);
watch.stop();
System.out.println(watch.prettyPrint());
System.out.println(parent);
System.out.println(clone);
Assert.assertNotEquals(clone,parent);
Assert.assertNotEquals(clone.getChild(),parent.getChild());
Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
}
@Test
public void testOrikaClone(){
watch.start();
Parent clone = OrikaMapperUtils.map(parent,Parent.class);
watch.stop();
System.out.println(watch.prettyPrint());
System.out.println(parent);
System.out.println(clone);
Assert.assertNotEquals(clone,parent);
Assert.assertNotEquals(clone.getChild(),parent.getChild());
Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
}
/**
* 需要类实现 Serializable
*/
@Test
public void testSerializationClone() {
watch.start();
Parent clone = SerializationUtils.clone(parent);
watch.stop();
System.out.println(watch.prettyPrint());
System.out.println(parent);
System.out.println(clone);
Assert.assertNotEquals(clone,parent);
Assert.assertNotEquals(clone.getChild(),parent.getChild());
Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
}
@Test
public void testJsonClone(){
watch.start();
JSONObject parse = JSONUtil.parseObj(parent);
Parent clone = JSONUtil.toBean(parse, Parent.class);
watch.stop();
System.out.println(watch.prettyPrint());
System.out.println(parent);
System.out.println(clone);
Assert.assertNotEquals(clone,parent);
Assert.assertNotEquals(clone.getChild(),parent.getChild());
Assert.assertNotEquals(clone.getChildren(),parent.getChildren());
Assert.assertNotEquals(clone.getChildrenList(),parent.getChildrenList());
}
}
8. 效率总结
数据来源:https://zhuanlan.zhihu.com/p/260117694
没有测试kryo序列化,看起来效率也还可以
* 深度拷贝类型 循环次数[1000] 循环次数[10000] 循环次数[1000000]
* new 5 ms 14 ms 133 ms
*
* Cloneable: < 1 ms 7 ms 88 ms
*
* Jdk序列化: 272 ms 1589 ms 66190 ms
*
* Kryo序列化: 95 ms 123 ms 2438 ms
*
* Json序列化: 1203 ms 3746 ms 163512 ms
总结: 1)、序列化性能 Clone > new > Kryo序列化 > Jdk序列化 > Json(各种Json类似)序列化
2)、Clone深拷贝性能最高,但是如果属性中有特定的对象字段,则需要自己编写代码
3)、new 性能仅次于Clone,因为需要执行Jvm过程(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等),并且主要是每个对象需要单独编写代码,当然也不建议使用反射
4)、kryo 性能较高,并且不需要单独的开发, 若对性能不是特别高,可以考虑使用.(kryo是非线程安全的,项目中使用时可以放入ThreadLocal中)
5)、Jdk序列化和Json序列化,性能太低,高性能项目不建议使用
==如果性能要求特别高(或者对象结构层次不深),可以使用Clone方式;否则可以考虑使用 Kryo序列化和反序列化实现对象深拷贝==