FastByteArrayOutputStream和ByteArrayOutputStream来深拷贝对象,到底谁的效率高呢?

原型模式中要实现深拷贝对象,一般是要重写clone()方法,因为有一些对象是没有实现Cloneable接口的,比如String,Integer,BigDecimal,这些常用的类都没有实现Cloneable接口。
但是很多情况下,如果重写clone()方法实现深拷贝,又会重新new对象(为了使引用对象指向是一个新的内存地址),像下面这样:

 @Override
    protected User clone() throws CloneNotSupportedException {
        User cloneUser = (User) super.clone();
        cloneUser.setAge(new Integer(this.age));
        cloneUser.setId(new Long(this.id));
        cloneUser.setMoney(new BigDecimal(this.money.toString()));
        cloneUser.setName(new String(this.name));
        return cloneUser;
    }

这就有点违背了原型模式的初衷,原型本来就是为了避免new对象时进行复杂的构造,这里重写clone()不仅没有避免,并且有new对象了。

这种情况下,我们就要使用对象序列化的方式来实现深拷贝,而最为推荐的是FastByteArrayOutputStream和ByteArrayOutputStream对象来实现深拷贝。
那么他们到底效率怎么样呢?
上代码:

这是要拷贝的对象:

public class User implements Serializable,Cloneable{

    private Integer age;

    private String name;

    private Long id;

    private BigDecimal money;

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(age, user.age) &&
                Objects.equals(name, user.name) &&
                Objects.equals(id, user.id) &&
                Objects.equals(money, user.money);
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, name, id, money);
    }


    public User() {
        this.age = 0;
        this.name = "";
        this.id = 1L;
        this.money = BigDecimal.ZERO;
    }

    public User(Integer age, String name, Long id, BigDecimal money) {
        this.age = age;
        this.name = name;
        this.id = id;
        this.money = money;
    }
    
}

下面是拷贝工具类:

public class  GetModelUtil {

    private static FastByteArrayOutputStream fastByteArrayOutputStream;

    private static ObjectOutputStream outputStreamOfFast;

    private static ObjectInputStream objectInputStreamOfFast;

    

    private static ByteArrayOutputStream commonbyteArrayOutputStream;

    private static ByteArrayInputStream commonbyteArrayInputStream;

    private static ObjectOutputStream outputStreamOfCommon;

    private static ObjectInputStream objectInputStreamOfCommon;

    // 为了提高效率,事先初始化内存输出对象
    static {
        fastByteArrayOutputStream = new FastByteArrayOutputStream();
        commonbyteArrayOutputStream = new ByteArrayOutputStream();
    }


	// FastByteArrayOutputStream 拷贝
    public static Object fastDeepClone(Object orig) throws IOException {
        Object obj = null;
        try {
            // 初始化 ObjectOutputStream 输出流对象
            outputStreamOfFast = new ObjectOutputStream(fastByteArrayOutputStream);
            // 写入对象到字节数组中,进行序列化
            outputStreamOfFast.writeObject(orig);
            // 刷新输出流
            outputStreamOfFast.flush();
            //从这个拷贝对象的流中 获取输入流
            objectInputStreamOfFast = new ObjectInputStream(fastByteArrayOutputStream.getInputStream());
            //从输入流中读出这个对象
            obj = objectInputStreamOfFast.readObject();
            // 因为测试的时候要多次读写,所以不关闭输入流和输出流
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        //返回拷贝的对象
        return obj;
    }
    
	// ByteArrayOutputStream 拷贝
    public static Object commonDeepClone(Object orig) throws IOException {
        Object obj = null;
        try {
            // 初始化 ObjectOutputStream 输出流对象
            outputStreamOfCommon = new ObjectOutputStream(commonbyteArrayOutputStream);
            // 将指定的对象写入字节数组输出,进行序列化
            outputStreamOfCommon.writeObject(orig);
            // 刷新输出流
            outputStreamOfCommon.flush();

            // 使用字节数组初始化 ByteArrayInputStream 输入流对象
            commonbyteArrayInputStream = new ByteArrayInputStream(commonbyteArrayOutputStream.toByteArray());
            // 使用字节数组初始化 ByteArrayInputStream 输入流对象
            objectInputStreamOfCommon = new ObjectInputStream(commonbyteArrayInputStream);
            // 从内存中读出对象
            obj = objectInputStreamOfCommon.readObject();
            // 因为测试的时候要多次读写,所以不关闭输入流和输出流
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        //返回拷贝的对象
        return obj;
    }

}

测试代码:

public static void main(String[] args) throws IOException, CloneNotSupportedException {
        User user = new User();
        user.setAge(11);
        user.setId(12L);
        user.setName("花花");
        long commonCloneStart = System.currentTimeMillis();
        for (int i = 0 ; i <= 1000 ; i++){
            User userOfClone = (User) GetModelUtil.commonDeepClone(user);
        }

        System.out.println("使用ByteArrayOutputStream深克隆花费时间:" + (System.currentTimeMillis() - commonCloneStart));

        long fastCloneStart = System.currentTimeMillis();
        for (int i = 0 ; i <= 1000 ; i++){
            User userOfClone = (User) GetModelUtil.fastDeepClone(user);
        }

        System.out.println("使用FastByteArrayOutputStream深克隆花费时间:" + (System.currentTimeMillis() - fastCloneStart));
    }

运行结果:

使用ByteArrayOutputStream深克隆花费时间:224
使用FastByteArrayOutputStream深克隆花费时间:57

很明显,使用FastByteArrayOutputStream拷贝对象效率明显高于ByteArrayOutputStream。
原因:
FastByteArrayOutputStream内部实现由一个LinkedList<byte[]>组成,每一次扩容中分配一个数组的空间,并当该数据放入到List中。需要分配的数组长度为调用FastByteArrayOutputStream的write方法决定。而ByteArrayOutputStream内部实现为一个数组每一次扩容需要重新分配空间并将数据复制到新数组中,这就是FastByteArrayOutputStream比ByteArrayOutputStream主要区别。

FastByteArrayOutputStream 部分源码

public class FastByteArrayOutputStream extends OutputStream {

	private static final int DEFAULT_BLOCK_SIZE = 256;

	// The buffers used to store the content bytes
	private final LinkedList<byte[]> buffers = new LinkedList<>();
	...
}

ByteArrayOutputStream部分源码:

public class ByteArrayOutputStream extends OutputStream {

    /**
     * The buffer where data is stored.
     */
    protected byte buf[];
	...
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中深拷贝对象可以使用Java序列化来实现。具体实现步骤如下: 1. 将待拷贝对象序列化为字节数组; 2. 将字节数组反序列化为新的对象。 以下是一个简单的深拷贝对象工具类: ``` import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class DeepCopyUtil { public static <T> T deepCopy(T obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (T) ois.readObject(); } } ``` 使用方法: ``` MyObject obj1 = new MyObject(); MyObject obj2 = DeepCopyUtil.deepCopy(obj1); ``` 需要注意的是,MyObject类必须实现Serializable接口才能被序列化。 ### 回答2: 深拷贝是指在复制对象时,不仅复制对象本身,还复制对象所引用的其他对象,使得原对象和拷贝对象完全独立,互不影响。在Java中,我们可以通过以下方式实现深拷贝对象的工具类: 1. 首先,确保需要复制的对象以及其引用的对象都实现了Serializable接口,这是Java提供的用于对象序列化的接口。这个接口没有任何方法,只是一个标识接口,用于告诉Java虚拟机该对象可以被序列化和反序列化。 2. 创建一个工具类,命名为DeepCopyUtils,该工具类提供静态方法用于进行深拷贝操作。 3. 在DeepCopyUtils中,创建一个方法,命名为deepCopy,该方法传入一个需要深拷贝对象作为参数,并返回一个新的拷贝对象。 4. 在deepCopy方法中,使用输入流和输出流的方式实现对象的序列化和反序列化。首先,创建一个ByteArrayOutputStream对象outputStream,用于将对象序列化为字节数组。然后,创建一个ObjectOutputStream对象objectOutputStream,将对象写入outputStream中。 5. 接着,创建一个ByteArrayInputStream对象inputStream,将之前序列化得到的字节数组作为参数传入。然后,创建一个ObjectInputStream对象objectInputStream,将inputStream传入对象输入流,用于反序列化。 6. 最后,通过objectInputStream的readObject方法,将反序列化得到的对象返回。 7. 在使用时,我们可以通过调用DeepCopyUtils.deepCopy方法,并将需要深拷贝对象作为参数传入,即可得到一个新的拷贝对象。 通过以上步骤,我们可以实现一个Java深拷贝对象的工具类,该工具类可以复制对象及其引用的其他对象,从而实现完全独立的拷贝对象。 ### 回答3: Java中的深拷贝对象工具类可以通过以下方式实现: 1. 首先,确保要拷贝的对象和它的所有引用类型成员变量都实现了`Serializable` 接口,这是因为深拷贝需要将对象序列化为字节流来进行拷贝。 2. 创建一个包含静态方法的工具类,该方法接收一个要拷贝的对象,并返回一个拷贝后的对象。 3. 在该方法内部,创建一个新的`ByteArrayOutputStream`对象和一个`ObjectOutputStream`对象。 4. 将要拷贝的对象写入到`ObjectOutputStream`中,然后将其转化为字节数组,并关闭流。 5. 创建一个`ByteArrayInputStream`对象和一个`ObjectInputStream`对象。 6. 使用`ObjectInputStream`从字节数组中读取对象,并将读取到的对象返回。 这样就完成了一个简单的深拷贝对象工具类。以下是示例代码: ```java import java.io.*; public final class DeepCopyUtils { private DeepCopyUtils() { // 私有构造函数,禁止实例化 } /** * 对象深拷贝方法 * * @param obj 要拷贝的对象 * @return 拷贝后的对象 * @throws IOException IO异常 * @throws ClassNotFoundException 类找不到异常 */ public static Object deepCopy(Object obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.close(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Object copy = ois.readObject(); ois.close(); return copy; } } ``` 使用该工具类,你可以对任意复杂的对象进行深拷贝,而不需要手动实现`Cloneable`接口和重写`clone()`方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值