JAVA的DIFF算法

news2024/9/25 23:20:21

首先看一下我的文件结构
文件目录结构

1.EnumType 类
public enum EnumType {
    ADD("ADD"),MODIFIED("MODIFIED"), DELETED("DELETED");
    //创建私有变量
    private String type;
    EnumType(String type) {
        this.type = type;
    }
}
2.OperationType类
public class OperationType {
    private static final EnumType ADD=EnumType.ADD;
    private static final EnumType MODIFIED=EnumType.MODIFIED;
    private static final EnumType REMOVED=EnumType.DELETED;
}
3.DiffListUtil类
public class DiffListUtil {
    @Data
    public static class TargetWrapper<T> {
        private T target;
        private EnumType type;

        public TargetWrapper(T target, EnumType type) {
            this.target = target;
            this.type = type;
        }

        // Getters and setters for target and type
    }
    @Data
    @Accessors(chain = true)
    public static class DiffResult<T> {
        private List<TargetWrapper<T>> allList;
        /**
         * 新增对象列表
         */
        private List<TargetWrapper<T>> addedList;
        /**
         * 修改后的对象列表
         */
        private List<TargetWrapper<T>> changedList;
        /**
         * 已删除对象列表
         */
        private List<TargetWrapper<T>> deletedList;
    }

    /**
     * 对比两个List的元素
     * <p>
     * 如果 baseList 的元素在 targetList 中存在 PrimaryKey 相等的元素并且 elementComparator 比较结果不相等,则将修改后的值添加到changedList列表中;
     * 如果 baseList 的元素在 targetList 中不存在,将baseList中的元素添加到deletedList中;
     * 如果 targetList 的元素在 baseList 中不存在,将targetList中的元素添加到addedList中;
     * <p>
     * complexity: O(n)
     *
     * @param baseList            基础List(原来的List)
     * @param targetList          目标List(最新的List)
     * @param elementComparator   元素比较器
     *primaryKeyExtractor
     * @param <T>
     * @return 对比结果
     */
    public static <T> DiffResult<T> diffList(List<T> baseList,
                                             List<T> targetList,
                                             @NotNull Function<T, Object> primaryKeyExtractor,
                                             @NotNull Comparator<T> elementComparator) {

        DiffResult<T> checkResult = checkEmptyAndReturn(baseList, targetList);
        if (checkResult != null) {
            return checkResult;
        }

        Map<Object,T> baseMap = new HashMap<>(4096);
        for(T base : baseList){
            Object key = primaryKeyExtractor.apply(base);
            baseMap.put(key,base);
        }

        List<TargetWrapper<T>> addedList = new ArrayList<>();
        List<TargetWrapper<T>> changedList = new ArrayList<>();
        List<TargetWrapper<T>> deletedList = new ArrayList<>();
        List<TargetWrapper<T>> allList = new ArrayList<>();

        //找出新增的 和需要更新的
        for (T target : targetList) {
            Object key = primaryKeyExtractor.apply(target);
            T base = baseMap.get(key);
            if(base == null){
                addedList.add(new TargetWrapper<T>(target, EnumType.ADD));
            }else{
                baseMap.remove(key);
                if (elementComparator.compare(base, target) != 0) {
                    changedList.add(new TargetWrapper<T>(target, EnumType.MODIFIED));
                }
            }
        }

        //剩余的就是需要删除的
        Set<Map.Entry<Object, T>> entrySet = baseMap.entrySet();
        if(CollUtil.isNotEmpty(entrySet)){
            for(Map.Entry<Object, T> entry:entrySet){
                deletedList.add(new TargetWrapper<T>(entry.getValue(), EnumType.DELETED));
            }
        }
        allList.addAll(addedList);
        addedList.addAll(changedList);
        addedList.addAll(deletedList);
        return new DiffResult<T>()
                .setAddedList(addedList)
                .setChangedList(changedList)
                .setDeletedList(deletedList)
                .setAllList(allList);
    }

    private static <T, V> void setFieldValue(T object, Function<? super T,V> fieldGetter, String value) {
        try {
            Field field = fieldGetter.getClass().getDeclaredField("value");
            field.setAccessible(true);
            field.set(fieldGetter.apply(object), value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 检查baseList 和 targetList 为empty(null||size==0)的情况
     *
     * @param baseList
     * @param targetList
     * @param <T>
     * @return
     */
    private static <T> DiffResult<T> checkEmptyAndReturn(List<T> baseList, List<T> targetList) {

        if (CollUtil.isEmpty(baseList) && CollUtil.isEmpty(targetList)) {
            return new DiffResult<T>()
                    .setAddedList(null)
                    .setChangedList(null)
                    .setDeletedList(null);
        }

        if (CollUtil.isEmpty(baseList) && CollUtil.isNotEmpty(targetList)) {
            List<TargetWrapper<T>> wrapperTargetList = targetList.stream().map(t -> new TargetWrapper<>(t, EnumType.DELETED)).collect(Collectors.toList());
            return new DiffResult<T>()
                    .setAddedList(wrapperTargetList)
                    .setChangedList(null)
                    .setDeletedList(null);
        }

        if (CollUtil.isNotEmpty(baseList) && CollUtil.isEmpty(targetList)) {
            List<TargetWrapper<T>> wrapperBaseList = baseList.stream().map(t -> new TargetWrapper<>(t, EnumType.DELETED)).collect(Collectors.toList());
            return new DiffResult<T>()
                    .setAddedList(null)
                    .setChangedList(null)
                    .setDeletedList(wrapperBaseList);
        }
        return null;
    }

    @Data
    @AllArgsConstructor
    public static class User {
        private Integer id;
        private String userName;
        private String address;
        private String email;
    }
}
4.ObjectComparator类
public class ObjectComparator<T> implements Comparator<T> {
    @Override
    public int compare(T o1, T o2) {
        // 反射来动态获取对象的属性
        Field[] fields = o1.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value1 = field.get(o1);
                Object value2 = field.get(o2);
                if (!isEqual(value1, value2)) {
                    return compareValues(value1, value2);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return 0;
    }

    private int compareValues(Object value1, Object value2) {
        if (value1 == null && value2 == null) {
            return 0;
        }
        if (value1 == null) {
            return -1;
        }
        if (value2 == null) {
            return 1;
        }
        if (value1 instanceof Comparable && value2 instanceof Comparable) {
            return ((Comparable) value1).compareTo(value2);
        }
        return 0;
    }

    private boolean isEqual(Object value1, Object value2) {
        if (value1 == null && value2 == null) {
            return true;
        }
        if (value1 == null || value2 == null) {
            return false;
        }
        return value1.equals(value2);
    }
}
5.Test单元测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class DiffListUtilApplicationTest {
    @Test
    public void test1() {
        List<DiffListUtil.User> userList = new ArrayList<>();
        DiffListUtil diffListUtil = new DiffListUtil();
        userList.add(new DiffListUtil.User(11,"John","hunan","hunan@faw.com"));
        userList.add(new DiffListUtil.User(22,"Tom","jilin","jilin@faw.com"));
        List<DiffListUtil.User> userListAfter = new ArrayList<>();
        userListAfter.add(new DiffListUtil.User(33,"John","hunan","beijing@faw.com"));
        userListAfter.add(new DiffListUtil.User(22,"Wudaiming","hunan","hunan@faw.com"));
        Function<DiffListUtil.User, Object> primaryKeyExtractor = user -> user.getId();
        //Comparator<DiffListUtil.User> userComparator = Comparator
        //        .comparing(DiffListUtil.User::getId)
        //        .thenComparing(DiffListUtil.User::getUserName)
        //        .thenComparing(DiffListUtil.User::getAddress)
        //        .thenComparing(DiffListUtil.User::getEmail);
        ObjectComparator<DiffListUtil.User> userComparator = new ObjectComparator<>();
        DiffListUtil.DiffResult<DiffListUtil.User> userDiffResult = diffListUtil.diffList(userList, userListAfter, primaryKeyExtractor, userComparator);
        System.out.println(userDiffResult);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/709852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue封装svg组件来修改svg图片颜色

文章目录 1、引入依赖2、根目录的vue.config.js配置3、在组件文件夹(compontents)中创建svgIcon.vue4、在src目录下创建icons文件5、处理svg格式的图片6、在main.js文件中引入icons文件中的index.js文件7、使用8、效果图1、项目成功运行后的样子2、直接在html上添加样式&#x…

DEBUG系列三:使用 F9 和 watch point

首先是我随便找了个报错。 报销消息号信息&#xff1a; No pricing procedure could be determined Message No. V1212 1&#xff09;首先可以直接SE91 来追溯这个消息号哪儿报出来的 可以看到下面两个地方可能会报这个消息&#xff0c;可以直接在这两个地方打断点&#xff0c;…

开发一个RISC-V上的操作系统(一)—— 环境搭建

在前面我们使用Verilog实现了一个简易的RISC-V处理器&#xff0c;并且能烧录到板子上跑一些简单C程序&#xff0c;传送门&#xff1a; RISC-V处理器的设计与实现&#xff08;一&#xff09;—— 基本指令集_risc_v处理器_Patarw_Li的博客-CSDN博客 RISC-V处理器的设计与实现&…

电子器件系列41:扁平高压电阻

这种电阻和其他的高压电阻不同&#xff0c;不是绕线电阻而是陶瓷电阻 找到一个大神&#xff0c;他的专栏也得很详细了&#xff0c;贴在这里 https://blog.csdn.net/wkezheng/category_12059870.html 阻容感基础03&#xff1a;电阻器分类&#xff08;1&#xff09;-片式电阻器…

如何快速判断是否在容器环境

在渗透测试过程中&#xff0c;我们的起始攻击点可能在一台虚拟机里或是一个Docker环境里&#xff0c;甚至可能是在K8s集群环境的一个pod里&#xff0c;我们应该如何快速判断当前是否在容器环境中运行呢&#xff1f; 当拿到shell权限&#xff0c;看到数字和字母随机生成的主机名…

软考A计划-系统集成项目管理工程师-项目范围管理(二)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

HTML、Markdown、Word、Excel等格式的文档转换为PDF

工具&#xff1a;gotenberg&#xff0c;docker部署 github&#xff1a;https://github.com/gotenberg/gotenberg 文档&#xff1a;https://gotenberg.dev/docs/about https://gotenberg.dev/docs/modules/libreoffice docker运行&#xff1a; docker run -d --rm -p 3000:30…

kubernete部署prometheus监控sring-boot程序

目录 1、kubernete集群环境以及prometheus基础环境 2、kubernetes监控集群内部的spring-boot程序 2.1、application.yml系统配置&#xff0c;endpoints相关设置 2.2、引入监控的相关依赖文件 pom.xml &#xff0c;主要是spring-boot-starter-actuator和micrometer-registr…

ModaHub魔搭社区:向量数据库Milvus产品问题(二)

目录 为什么向量距离计算方式是内积时&#xff0c;搜索出来的 top1 不是目标向量本身&#xff1f; 对集合分区的查询是否会受到集合大小的影响&#xff0c;尤其在集合数据量高达一亿数据量时&#xff1f; 如果只是搜索集合中的部分分区&#xff0c;整个集合的数据会全部加载…

表单(form) post 方式提交时的编码与乱码(上)

在上一篇章中谈论了表单以 get 提交时的编码与乱码问题, 这一章中将讨论以 post 方式提交时的编码与乱码问题. 在前面也同时提到, 表单有一个叫 enctype 的属性, 它有两个值, application/x-www-form-urlencoded 和 multipart/form-data. 这一属性实际只对 post 方式起作用, …

@Configuration 和 @Component 的区别 ,别再瞎用了!

一句话概括就是 Configuration 中所有带 Bean 注解的方法都会被动态代理&#xff0c;因此调用该方法返回的都是同一个实例。 理解&#xff1a;调用Configuration类中的Bean注解的方法&#xff0c;返回的是同一个示例&#xff1b;而调用Component类中的Bean注解的方法&#xff…

List, Set, Ordered-SetHash

前言 本文小结Redis中List&#xff0c;Set&#xff0c;ZSet和Hash四种数据类型的&#xff0c;基本特点&#xff0c;使用场景和实现方式。 一、List 1. 基本特点 a. 作为数组&#xff0c;基于下标索引操作, 但支持正向索引和反向索引; b. 作为链表, 支持高效插入&#xff1b…

信息安全-应用安全-定制化白盒检测 | 越权漏洞治理分享

目录 一、背景 二、面临的挑战 三、治理目标 四、解决方案 4.1 系统架构 4.2 鉴权函数 4.3 告警识别 4.4 鉴权分 五、未来的白盒检测方向 六、越权治理 七、小结 一、背景 在漏洞扫描领域&#xff0c;主流的扫描方式分为黑盒扫描和白盒扫描&#xff0c;其中源代码安…

MYSQL-主键外键约束

主键语法: 在创建表指定列数据类型时在后面加(可以结合AUTO_INCREMENT) PRIMARY KEY 主键要短&#xff0c;可唯一标识记录&#xff0c;且永不改变。 外键语法: 第一个column_name是被指定外键的本表列名 table_name是主键的表名 第二个column_name是主键列名 FOREIGN KE…

使用DataX同步数据(小白步骤,一看就懂)

详细文档说明,及图文讲解 ​​​​​​datax的异构数据同步资源-CSDN文库 Datax简介 下载datax软件,从开源镜像下载

python接口自动化(七)--状态码详解对照表(详解)

简介 我们为啥要了解状态码&#xff0c;从它的作用&#xff0c;就不言而喻了。如果不了解&#xff0c;我们就会像个无头苍蝇&#xff0c;横冲直撞。遇到问题也不知道从何处入手&#xff0c;就是想找别人帮忙&#xff0c;也不知道是找前端还是后端的工程师。 状态码的作用是&…

串口接收不定长数据的实现

使用串口进行数据的收发在嵌入式产品中是很常用的一种通信方式&#xff0c;因为串口的简单使用&#xff0c;很容易就被选为产品中数据交互的通信手段。 基于串口进行开发的功能有很多&#xff0c;比如同类/不同类产品之间的通信&#xff0c;RS485通信&#xff0c;RS232通信方式…

(二)线程的六种状态及上下文切换

&#xff08;二&#xff09;线程的六种状态及上下文切换 2.1 操作系统中线程的状态及切换2.2 Java 中线程的六种状态01、NEW&#xff08;线程尚未启动&#xff09;02、RUNNABLE&#xff08;运行中&#xff09;03、BLOCKED&#xff08;阻塞状态&#xff09;04、WAITING&#xff…

移动端微信小程序学习

目录 小程序和web端的不同 小程序的宿主环境 通信 组件 视图容器​编辑 text组件 button image ​编辑 API api三大分类 模板语法 事件绑定 ​编辑 事件传参​编辑 bindinput 条件渲染 列表渲染 ​编辑 全局配置 window 页面配置 网络数据请求 ​编辑 GET请求 POST…