那些年踩过的坑之对象拷贝

一、前言

        前段时间小永哥分享了有关redis的一些常用技巧,小永哥打算以java实现lru算法作为redis篇的结尾,不是说redis只有这点东西,有两点原因,第一是小永哥资源有限,像哨兵模式、集群、哨兵模式也没有从0到1的搭建过程,只谈理论没有操作我也不会写,缺乏实践容易出坑,误导读友们,第二就是我水平有限,再写下去我容易把我不住,等水平再提升提升之后看再补上后续这些高阶的东西。

        最近几天暂时先发几篇开发过程的小技巧和注意事项,顺便思考一下下一步写作的方向,这次先分享一篇关于对象拷贝的事情。

二、场景复现

        先来说一个业务场景,我当时开发过一个功能,两颗树形数据比较差异,如果对象A在A树上存在而在B树上不存在,那么就将A对象也在B树中设置一份并且增加一个空行状态标识,到时候前端就会根据这个标识来渲染哪些数据是自身缺失的,为了方便测试,我们这次只弄两个集合进行模拟,来实现这个场景,用于引出我们后续的概念。

package com.relation;

import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author huhy
 * @version 1.0
 * @Description:
 * @ClassName Date:2025/4/20 21:11
 */
public class CloneTest {
    @Test
    public void testCompare() throws Exception{
        Map<String,Object> attr = new HashMap<>();
        attr.put("status","正常");
        List<TestDemo> oneTestDemoList = new ArrayList<>();
        oneTestDemoList.add(new TestDemo("测试T01","T01-CODE",attr));
        oneTestDemoList.add(new TestDemo("无法无天","WFWT-CODE",attr));
        oneTestDemoList.add(new TestDemo("小明","XIAOMING",attr));
        List<TestDemo> twoTestDemoList = new ArrayList<>();
        twoTestDemoList.add(new TestDemo("测试T01","T01-CODE",attr));
        twoTestDemoList.add(new TestDemo("巨阙","JUQUE",attr));
        twoTestDemoList.add(new TestDemo("小明","XIAOMING",attr));
        //比较差异
        listCompare(oneTestDemoList,twoTestDemoList);
        listCompare(twoTestDemoList,oneTestDemoList);
        //分别打印两个集合
        System.out.println("oneTestDemoList: "+JSON.toJSONString(oneTestDemoList));
        System.out.println("twoTestDemoList: "+JSON.toJSONString(twoTestDemoList));
    }

    /**
     * 功能描述:
     * @param:比较两个集合的差异,将缺失的对象补齐并修改状态为空行
     * @Author:huhy
     * @Date: 2025/4/21 22:21
     */
    private void listCompare(List<TestDemo> oneTestDemoList,
                             List<TestDemo> twoTestDemoList)throws Exception{
        for (TestDemo testDemo : oneTestDemoList) {
            if(!check(testDemo,twoTestDemoList)){
                testDemo.getAttr().put("status","空行");
                twoTestDemoList.add(testDemo);
            }
        }
    }

    private boolean check(TestDemo testDemo,List<TestDemo> testDemoList){
        String code = testDemo.getCode();
        for (TestDemo demo : testDemoList) {
            if(demo.getCode().equals(code)){
                return true;
            }
        }
        return false;
    }

}

        我们可以看到,【无法无天】这个对象,在集合2中缺失,所以将【无法无天】补全到集合2中设置为空行没问题,但是在集合1和集合2中状态全都变成了空行,这就不对,这个得排查一下代码。

        有问题的代码也好找,就是下图中被框住的代码,我们找到差异之后,不能直接修改差异对象,应该将差异对象复制一份再进行操作。

      ​​

        我们对问题代码进行修改再运行看看效果。

private void listCompare(List<TestDemo> oneTestDemoList,
                             List<TestDemo> twoTestDemoList)throws Exception{
        for (TestDemo testDemo : oneTestDemoList) {
            if(!check(testDemo,twoTestDemoList)){
                TestDemo copyTestDemo = testDemo.clone();
                copyTestDemo.getAttr().put("status","空行");
                twoTestDemoList.add(copyTestDemo);
            }
        }
    }

        好像没什么效果,不应该呀,我们已经拷贝了对象,为什么还是没效果,这个问题其实很简单,就是浅拷贝和深拷贝的问题,我们来看一下这两者的区别:

        浅拷贝:浅拷贝创建一个新对象,但是会将原对象中的非基本类型字段的引用复制到新对象中。如果原对象中的非基本类型字段被更改,这些更改也会反映在新对象中。

        深拷贝:对于深拷贝,你需要确保所有非基本类型的字段都被复制了一份新的实例。这通常涉及到递归克隆或者使用序列化与反序列化技术。

        问题的根源就是浅拷贝造成的,所以我们使用深拷贝试一下效果,至于序列化和反序列化的技术,我本次使用JSON方式进行处理,如果大家对流感兴趣,可以试试用流进行序列化和反序列化。

    private void listCompare(List<TestDemo> oneTestDemoList,
                             List<TestDemo> twoTestDemoList)throws Exception{
        for (TestDemo testDemo : oneTestDemoList) {
            if(!check(testDemo,twoTestDemoList)){
                TestDemo copyTestDemo = JSON.parseObject(JSON.toJSONString(testDemo),TestDemo.class);
                copyTestDemo.getAttr().put("status","空行");
                twoTestDemoList.add(copyTestDemo);
            }
        }
    }

        如图所示,空行状态设置已经正常了,小永哥尝试过,其实如果空行状态不是属性组而是一个字符串或者数字类型,那么浅拷贝也是可以的,但属性组是一个对象,浅拷贝无法彻底复制一份全新的副本,必须得用深度拷贝来实现。

三、结语

        这次分享的内容不是什么高深的东西,但万一遇到的话一定很恶心,因为演示代码我简化了逻辑复杂度所以可以一眼就能定位到,小永哥当时是两颗纵深不确定的树进行比较,在递归中进行差异比较,除了比较空行差异,如果不是空行差异,还要比较属性差异,在庞大的代码中快速定位问题还是很麻烦的,所以希望大家能够在平时的时候就养成好习惯,争取在写代码的时候就能提高准确度,减少bug,好了,本次就到这里了,晚安。

        

        

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值