Android Kotlin Java list clone 深拷贝简单使用

先上通过 clone 深拷贝的使用步骤:

  1. model 先实现 Cloneable, 重写 clone 方法.
  2. model 调用 clone 就会生成一个 全新的对象.
  3. new 一个新数组 copyList, 遍历 原始数组 oriList, 一个一个添加到 copyList 即可.

talk is cheap, show me the code: 

class MainKotlin {
    @Test
    fun test0() {
        val oriList = ArrayList<Model>()
        val model0 = Model(0, "name0")
        val model1 = Model(1, "name1")
        val model2 = Model(2, "name2")
        oriList.add(model0)
        oriList.add(model1)
        oriList.add(model2)


        val copyList = ArrayList<Model>()
        oriList.forEach {
            copyList.add(it.clone())
        }
        println("list = $oriList")
        println("copyList = $copyList")

        println("======== change name ========")
        model0.name = "name0 changed"

        println("list = $oriList")
        println("copyList = $copyList")

    }

    class Model(): Cloneable {
        var id: Int? = null
        var name: String? = null

        constructor(id: Int, name: String) : this() {
            this.id = id
            this.name = name
        }

        // 添加 try catch, 防止奇怪的异常
        public override fun clone(): Model {
            return try {
                return super.clone() as? Model ?: Model()
            } catch (e: CloneNotSupportedException) {
                Model()
            }
        }

        override fun toString(): String {
            return "Model(id=$id, name=$name)"
        }
    }
}

输出结果: 

        可以看到 model0 更改 name 时, list 随之改变, copyList 没有改变, 成功~

起因: 

        今天在做单例的时候, 被数据的一些状态整懵了, 虽然想到了可以通过深拷贝解决问题, 但竟然直接使用了 newList.addAll(oldList) ... 结果又排查问题查了半天...才想到 list 中的 model 也是要完全 new 出来才行, model 里面那么多属性, 一个一个赋值太不优雅啦, 在网上查了下可以通过:

  1. clone 实现
  2. 实现序列化, 通过反序列化 new 出来对象
  3. 转成 json 字符串, 再转成对象(解析最好在子线程)

序列化深拷贝实现见 后续补充.

注意: 

        java的一个类,如果要使用 Cloneable 实现拷贝功能, 需要先实现这个接口, 然后重写Object的clone方法. 对于类中的引用类型的属性, 需要在clone方法中实现深拷贝, 否则就是浅拷贝.

        属性中有引用对象的话, 引用对象也要实现 Cloneable, 然后这个引用对象也要调用 clone 方法才行...有点麻烦, 得封装一下才好用...后续带封装...

后续待优化: 

        可以把 集合 的深拷贝封装一个函数, 这样调用起来比较方便, 待执行任务...

后续补充

序列化实现, 有两种, Serializable 和 Parcelable

Serializable 存在文件读写的过程, Parcelable 是在内存中操作, 建议使用 Parcelabel.

1. Serializable 实现

当前需要类实现 Serializable 接口, 并且如果成员变量有引用类型, 此引用类型也需要实现 Serializable 接口, 这样深拷贝才能成功

java 代码

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class DeepCopyUtil {
    public static Object deepClone(Serializable obj) {
        Object anotherObj = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            anotherObj = ois.readObject();
        } catch (Exception ex) {
            
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException localIOException3) {
                    Logger.e("deepClone error:"+localIOException3.getMessage());
                }
            }
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException localIOException4) {
                    
                }
            }
        }
        return anotherObj;
    }
}

kotlin 代码

fun <T : Serializable> T.deepCopy(obj: T?): T? {
    if (obj == null) return null
    val baos = ByteArrayOutputStream()
    val oos  = ObjectOutputStream(baos)
    oos.writeObject(obj)
    oos.close()
    val bais = ByteArrayInputStream(baos.toByteArray())
    val ois  = ObjectInputStream(bais)
    @Suppress("unchecked_cast")
    return ois.readObject() as T
}

2. Parcelable 实现

注意 parcelabel 需要在 android 环境中测试, 在 java 中会报异常 java.lang.RuntimeException: Stub!...

 参考 https://farhanpatel.dev/index.php/2020/06/14/deep-clones-with-android-parcelable/

fun <T : Parcelable> deepClone(objectToClone: T): T? {
        var parcel: Parcel? = null
        return try {
            parcel = Parcel.obtain()
            parcel.writeParcelable(objectToClone, 0)
            parcel.setDataPosition(0)
            parcel.readParcelable(objectToClone::class.java.classLoader)
        } finally {
            //it is important to recyle parcel and free up resources once done.
            parcel?.recycle()
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃饱很舒服

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值