集合的赋值和副本问题记录

最近在开发的过程中,遇到了对象集合List的操作问题,主要是涉及到引用对象集合list的操作修改问题,这个问题困扰了好几个小时,特此记录一下,以防以后可以迅速回忆,有类似情况的也可以给大家一个排查问题的思路。

一,问题背景:

1,需要2个集合list列表:一个用于保存已选择过的list数据(列表1),每次进入操作界面,需要标识已选择list item;一个用于记录当前操作选择的list数据(列表2),进入操作界面,需要用已选择过的list数据(列表1)进行先赋值,后面根据当前界面的操作修改 列表2 的数据;在退出操作界面的时候,将列表2的数据重置赋值给列表1;

2,2个列表的数据都是引用对象的list ,非基本类型(Int、Double等)的的list

3,使用的是kotlin的LiveData 记录 列表1 的数据,列表使用的mutableListOf()

二,问题描述:

这里引发了2个问题:

1,修改列表2的对象数据的某个成员变量时,会修改到列表1的数据;

2,在操作选择列表item项的时候选择或不选1个的时候,如果有引用到 列表2的数据 对方法内的局部变量list进行赋值,那么会触发到整个数据列表list LiveData的Observer监听数据变化失效。诡异的是,选择2个或者不选是正常的,能触发LiveData的Observer监听数据变化;

三,原因排查:

1,问题一这个原因很明显,是引用了同一个地址的列表数据,想起Java时候list.addAll()进行副本生成可以解决这个问题,然后就addAll()试了一下(注:kotlin代码),并没有生效,然后就查找如何生成list的副本,试了一通,并没有奏效,网上博主很多提到的操作都是基于基本数据类型操作有效的,对于对象列表list根本无效,这个有点坑,自己就写个小demo进行验证,毕竟实践是检查真理的唯一标准呢。

    data class Person(
        var name: String,
        var age: Int,
        var isStudent: Boolean,
    )

    fun test() {
        //测试 .toCollection(mutableListOf())  【基础类型有效】
        val original: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5);
        val copy: MutableList<Int> = original.toCollection(mutableListOf())
        copy.add(8)
        LogUtils.error("test## [toCollection] original: " + original)
        LogUtils.error("test## [toCollection] copy: " + copy)
 
        //测试 toMutableList()        【基础类型有效】
        val original2: MutableList<Int> = mutableListOf(11, 12, 13, 14, 15);
        val copy2: MutableList<Int> = original2.toMutableList()
        copy2.add(18)
        copy2.add(20)
        LogUtils.error("test## [toMutableList] original2: " + original2)
        LogUtils.error("test## [toMutableList] copy2: " + copy2)

        //测试 addAll()          【基础类型有效】
        val original3: MutableList<Int> = mutableListOf(21, 22, 23, 24, 25);
        val copy3: MutableList<Int> = mutableListOf<Int>()
        copy3.addAll(original3)
        copy3.add(2000)
        LogUtils.error("test## [addAll] original3: " + original3)
        LogUtils.error("test## [addAll] copy3: " + copy3)

        //测试对象 toMutableList   【无效】
        val original4 = mutableListOf<Person>(
            Person("dong", 20, true),
            Person("nan", 35, false),
            Person("xi", 50, false)
        )
        val copy4 = original4.toMutableList()
        copy4.get(2).isStudent = true
        LogUtils.error("test## [object-toMutableList] original4: " + original4)
        LogUtils.error("test## [object-toMutableList] copy4: " + copy4)

        //测试对象 addAll()  【无效】
        val original5 = mutableListOf<Person>(
            Person("hei", 10, true),
            Person("bai", 25, false),
            Person("hui", 40, false)
        )
        val copy5 = mutableListOf<Person>()
        copy5.addAll(original5)
        copy5.get(2).isStudent = true
        LogUtils.error("test## [object-addAll] original5: " + original5)
        LogUtils.error("test## [object-addAll] copy5: " + copy5)

        //测试对象 toCollection   【无效】
        val original6 = mutableListOf<Person>(
            Person("lan", 40, true),
            Person("lv", 55, false),
            Person("hong", 60, false)
        )
        val copy6 = original6.toCollection(mutableListOf())
        copy6.get(2).isStudent = true
        LogUtils.error("test## [object-toCollection] original6: " + original6)
        LogUtils.error("test## [object-toCollection] copy6: " + copy6)

        //测试对象 copy()  【注意:引用对象的时候有效】
        val original7 = mutableListOf<Person>(
            Person("one", 7, true),
            Person("two", 8, false),
            Person("free", 9, false)
        )
        val copy7 = mutableListOf(
            original7.get(0).copy(),
            original7.get(1).copy(),
            original7.get(2).copy()
        )
        copy7.get(2).isStudent = true
        LogUtils.error("test## [object-copy()] original7: " + original7)
        LogUtils.error("test## [object-copy()] copy7: " + copy7)

    }

输出结果如下:

 验证完,基本就是使用copy()的方式去生成对象副本

2,问题2的虽然也解决了,但是原因至今没有想明白,因为livedata的value是有赋值修改的,但是并没有回调到监听数据的Oberver监听回调里面,是完全没有回调进来,尝试过debug进入livedata的setvalue() 方法,一步步debug下来,并没有发现任何端倪。猜测可能的原因是,是LiveData的setvalue()方法对于判断数据变更监听做了暂时未知的判断(例如是否监听对象的地址的一致等);

     而问题2的解决原因是操作了列表2引起的,那么在选择操作时,不直接操作列表2的当前选中数据list不就可以了,那么怎么拿到当前已选择的列表数据,因为列表都是使用adaper适配器进行实现的,那么直接将adapter.data的数据进行读取出来进行数据赋值不就好了。

//#######################  修改前  ####################### 
val selectList = curSelectList  //将当前的数据list赋值给局部变量
//省略中间进行操作的代码 .... 
//......  期间 selectList 对象列表的数据会因为操作而变更
//....... 
curSelectList = selectList  //最后将修改的数据保存回成员变量 curSelectList

//####################### 修改后  ########################
val selectList = adapter.data.filter {
                     it.isStudent == true  //这里以Person对象为例子
                 }.toMutableList()  //不直接操作成员变量 curSelectList 
//省略中间进行操作的代码 .... 
//......  期间 selectList 对象列表的数据会因为操作而变更
//....... 
//curSelectList = selectList  //最后也不直接操作成员变量 curSelectList (放弃操作该成员变量)
//退出界面也是通过 adapter.data 进行读取选中的数据

四、解决方案:

1,基础数据类型可以通过toMutableList()等方法进行副本赋值;但是对象数据需要通过copy()进行副本赋值,否则会操作的是同一个地址的对象;

2,通过adapter.data的方式获取选中数据,而不通过成员变量进行记录和获取;

     ps:关于问题2中LiveData中的监听回调失效问题,各位有思路可以说说,我看到会进行实践验证。

五、总结:

1,有遇到列表数据操作修改,修改到了源数据的类似问题,可以借鉴参考一下

2,有时候使用别人封装的框架,例如jetpack的livedata,遇到一些诡异的异常时,往往会让你痛苦万分,这时候就明白,多好的框架都不如最基本的代码,或者自己实现的代码。有问题还能修改处理,不熟悉的框架只能食之无味弃之可惜的两难境地。

3,不要迷信任何代码权威,自己亲手去实践,自己写的东西才是最有力量的

             

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值