对象属性值对比(支持复杂对象)

news2024/9/24 1:23:37

文章目录

  • 前言
  • 一、如何对比
  • 二、开始编码
  • 三、使用结果示例
  • 总结


前言

需求如下:

对比两个bean中的内容,返回其中属性的值不一致的完整信息,包括: 属性 新值 旧值


一、如何对比

例如我有一个这的类型:

public class Tel {

  private String name;

  private String tel;
}

我new 两个出来,然后比对

Tel tel = new Tel("小明","123)
Tel tel2 = new Tel("小红","123)

比对后应该输出: name 小明 小红

二、开始编码

package xx.main;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.example.test.bean.Res;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 对线属性值对比
 *
 * @author: fulin
 * @since: 2024-08-02
 **/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCompareUtil {

    /**
     * 全量比对两个对线
     *
     * @param source 来源对象
     * @param target 目标对象
     * @return 对比结果
     */
    public static List<Res> compareBean(Object source, Object target) {
        List<Res> resList = CollUtil.newArrayList();
        handleCompare(source, target, resList, source.getClass().getSimpleName());
        return resList;
    }

    /**
     * 部分比对
     *
     * @param source   来源对象
     * @param target   目标对象
     * @param fileList 需要比对的字段
     * @return 对比结果
     */
    public static List<Res> compareBean(Object source, Object target, String... fileList) {
        List<Res> resList = CollUtil.newArrayList();
        handleCompare(source, target, resList, source.getClass().getSimpleName(), fileList);
        return resList;
    }

    private static void handleCompare(Object source, Object target, List<Res> resList, String className, String... fileList) {
        if (ObjectUtil.isNull(source) || ObjectUtil.isNull(target)) {
            return;
        }
        Field[] declaredFields = source.getClass().getDeclaredFields();
        if (ObjectUtil.isNotEmpty(declaredFields) && ObjectUtil.isNotEmpty(fileList)) {
            declaredFields = Arrays.stream(declaredFields).filter(field -> Arrays.stream(fileList).distinct().anyMatch(file -> file.equals(field.getName()))).toArray(Field[]::new);
        }

        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            Class<?> type = declaredField.getType();
            Object fieldValue = BeanUtil.getFieldValue(source, declaredField.getName());
            Object fieldValue2 = BeanUtil.getFieldValue(target, declaredField.getName());
            String classNameStr = className + StrUtil.DASHED + declaredField.getName();
            if (!ClassUtils.isPrimitiveOrWrapper(type) && !type.equals(String.class)) {
                if (fieldValue instanceof List) {
                    List<Object> list = (List<Object>) fieldValue;
                    List<Object> list2 = (List<Object>) fieldValue2;
                    for (int i = 0; i < list.size(); i++) {
                        handleCompare(list.get(i), list2.get(i), resList, classNameStr, fileList);
                    }
                } else if (fieldValue instanceof Map) {
                    Map<Object, Object> map = (Map<Object, Object>) fieldValue;
                    Map<Object, Object> map2 = (Map<Object, Object>) fieldValue2;
                    map.forEach((k, v) -> {
                        handleCompare(v, map2.get(k), resList, classNameStr, fileList);
                    });
                } else {
                    handleCompare(fieldValue, fieldValue2, resList, classNameStr, fileList);
                }
            } else {
                Object property = BeanUtil.getProperty(source, declaredField.getName());
                Object property2 = BeanUtil.getProperty(target, declaredField.getName());
                if (ObjectUtil.notEqual(property, property2)) {
                    log.info("字段: {},旧值:{},新值:{}", classNameStr, property, property2);
                    resList.add(new Res(declaredField.getName(), property, property2));
                }
            }
        }
    }

}

三、使用结果示例

根据一个复杂的类 ,构造两个属性值不同的对象,然后测试

package xx.main;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.test.bean.Res;
import com.example.test.bean.Student;
import com.example.test.bean.Tel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.util.ClassUtils;
import xx.bean.CpuInfo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author fulin
 * @since 2024/8/2 16:17
 */
@Slf4j
public class 比对 {


    public static void main(String[] args) {


        Tel tel = new Tel();
        tel.setTel("123456789");
        tel.setName("张3");


        Tel tel2 = new Tel();
        tel2.setTel("1234589");
        tel2.setName("张三");

        CpuInfo cpuInfo = new CpuInfo();
        cpuInfo.setCpu(2);
        cpuInfo.setMem(8);
        cpuInfo.setDisk(100);
        cpuInfo.setStudent(new Student(1, "张三", 45, Collections.singletonList(tel)));

        CpuInfo cpuInfo2 = new CpuInfo();
        cpuInfo2.setCpu(20);
        cpuInfo2.setMem(81);
        cpuInfo2.setDisk(100);
        cpuInfo2.setStudent(new Student(1, "李四", 41, Collections.singletonList(tel2)));


        System.out.println(JSONUtil.toJsonStr(cpuInfo));
        System.out.println(JSONUtil.toJsonStr(cpuInfo2));


        List<Res> resList = BeanCompareUtil.compareBean(cpuInfo, cpuInfo2);
        System.out.println(JSONUtil.toJsonStr(resList));
        List<Res> resList2 = BeanCompareUtil.compareBean(cpuInfo, cpuInfo2, "cpu", "mem");
        System.out.println(JSONUtil.toJsonStr(resList2));
    }
}

  • 执行结果如下:
    {"student":{"tels":[{"name":"张3","tel":"123456789"}],"name":"张三","id":1,"age":45},"cpu":2,"disk":100,"mem":8} {"student":{"tels":[{"name":"张三","tel":"1234589"}],"name":"李四","id":1,"age":41},"cpu":20,"disk":100,"mem":81} 20:54:07.624 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-cpu,旧值:2,新值:20 20:54:07.626 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-mem,旧值:8,新值:81 20:54:07.627 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-student-name,旧值:张三,新值:李四 20:54:07.627 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-student-age,旧值:45,新值:41 20:54:07.627 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-student-tels-name,旧值:张3,新值:张三 20:54:07.627 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-student-tels-tel,旧值:123456789,新值:1234589 [{"old":2,"file":"cpu","now":20},{"old":8,"file":"mem","now":81},{"old":"张三","file":"name","now":"李四"},{"old":45,"file":"age","now":41},{"old":"张3","file":"name","now":"张三"},{"old":"123456789","file":"tel","now":"1234589"}] 20:54:07.632 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-cpu,旧值:2,新值:20 20:54:07.632 [main] INFO xx.main.BeanCompareUtil - 字段: CpuInfo-mem,旧值:8,新值:81 [{"old":2,"file":"cpu","now":20},{"old":8,"file":"mem","now":81}]

执行结果


总结

找了很多工具类都没有找到,然后自己动手封装了一个,希望能给后来人带来参考;
虽说是自己封装的,但实际也是站在巨人的肩膀,基于 hutool jdk 等工具类

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

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

相关文章

学习笔记第十七天

1.链表 1.1链表尾插 void push_back(struct Node *pHead,int n)//尾插 {if(isEmpty(pHead)){push_front(pHead,n);}else{struct Node *p pHead->next; while(p->next !NULL){p p->next;}struct Node *pNew malloc(sizeof(struct Node));p->nextpNew;pNew->n…

C++ bind复杂回调逻辑分析

回调函数基本知识回顾 回调函数是什么 函数指针或者函数对象作为参数传递给另一个函数的机制&#xff0c;当某个事件发生的时候&#xff0c;系统会自动的调用这些函数进行处理事件驱动模型中作用&#xff0c;回调函数则被用于处理I/O事件&#xff0c;通常用来读写异常等事件 bi…

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——2Yolo使用之ONNX模型准备

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——2Yolo使用之ONNX模型准备 ​ 大家好&#xff0c;因为板端BPU环境&#xff0c;可以加速目标检测的速度&#xff0c;所以今天在此先给大家带来如何准备一个模型&#xff0c;下一期会给大家带来如何在板端部…

如何做一个惊艳领导和客户的原型?

在产品开发过程中&#xff0c;原型设计是验证设计想法、提升用户体验的重要环节。Axure作为一款业界领先的原型设计工具&#xff0c;凭借其强大的交互设计和丰富的功能&#xff0c;赢得了全球设计师和开发者的信赖。而Axure的高效交互元件库&#xff0c;则如同一本字典或说明书…

将YOLOv8模型从PyTorch的.pt格式转换为OpenVINO支持的IR格式

OpenVINO是Open Visual Inference & Neural Network Optimization工具包的缩写&#xff0c;是一个用于优化和部署AI推理模型的综合工具包。OpenVINO支持CPU、GPU和NPU设备。 OpenVINO的优势: (1).性能&#xff1a;OpenVINO利用英特尔CPU、集成和独立GPU以及FPGA的强大功能提…

原生IP节点是什么意思?和socks5节点有什么区别?

在了解这两种代理节点前&#xff0c;我们首先要了解&#xff1a;节点是什么&#xff1f; 首先&#xff0c;在电信网络当中&#xff0c;一个节点是一个连接点。表示一个再分发点又或者是一个通信端点。节点的定义依赖于所提及的网络和协议层。一个物理网络节点是一个连接到网络…

深度强化学习:穿越智能迷雾,探索AI新纪元

近年来&#xff0c;深度强化学习成为关注的热点。在自动驾驶、棋牌游戏、分子重排和机器人等领域&#xff0c;计算机程序能够通过强化学习&#xff0c;理解以前被视为超级困难的问题&#xff0c;取得了令人瞩目的成果。在围棋比赛中&#xff0c;AlphaGo接连战胜樊麾、李世石和柯…

使用 SpringBoot + 虚拟线程将服务性能提升几百倍

虚拟线程简介 虚拟线程是 Java 平台的一项创新特性。虚拟线程是一种轻量级的线程实现,它在操作系统层面并不对应真实的内核线程,而是由 JVM 进行管理和调度。这使得可以在不消耗大量系统资源的情况下创建大量的线程,从而能够更高效地处理并发任务。 虚拟线程与普通线程的区…

【数学建模】——【A题 信用风险识别问题】全面解析

目录 1.题目 2.解答分析 问题1&#xff1a;指标筛选 1.1 问题背景 1.2 数据预处理 1.3 特征选择方法 1.4 多重共线性检测 1.5 实现步骤 问题2&#xff1a;信用评分模型 2.1 问题背景 2.2 数据分割 2.3 处理不平衡数据 2.4 模型选择与理由 问题3&#xff1a;模型对…

『 Linux 』线程池与 POSIX 线程的封装编码实现

文章目录 线程池概念线程池的编码实现线程池的测试参考代码 线程的封装使用测试封装后的线程参考代码 线程池概念 池化技术是一种资源管理方法,通过预先创建和管理一组资源以便在需要使用时快速分配这些资源; 线程池是池化技术的一种典型应用; 资源分配 在线程池中预先创建一定…

【python015】常见成熟AI-图像识别场景算法清单(已更新)

1.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码文章目录 1.背景介绍2.`Python`版数据爬取、解析代码2.…

鸿蒙应用框架开发【画中画效果实现】 UI框架

画中画效果实现 介绍 本示例通过kit.ArkUI、kit.MediaKit等接口&#xff0c;实现了视频播放、手动和自动拉起画中画、画中画窗口控制视频播放和暂停等功能。 效果预览 使用说明 在主界面&#xff0c;可以点击对应视频按钮进入视频播放页面&#xff1b;视频播放页面点击开启…

三星One UI 7.0引入苹果的几大特色功能,iOS用户都羡慕哭了

在智能手机操作系统的创新之路上&#xff0c;苹果iOS系统的Dynamic Island和Live Activities功能无疑为用户带来了全新的交互体验。 现在&#xff0c;三星One UI 7.0系列的即将发布&#xff0c;似乎预示着安卓阵营也将迎头赶上&#xff0c;甚至可能在某些方面超越苹果。以下是…

AcWing-AcWing 837. 连通块中点的数量

在原来并查集的基础上增加一个Size数组&#xff0c;Size的初始化是必须先每个元素初始化为1 Size只对根节点有效&#xff0c;比如Size[find(1)]就是找1的祖先节点&#xff0c;然后访问祖先节点的个数。当我们联通a点和b点时&#xff0c;如果已经是联通状态了&#xff0c;那么无…

深度解读:等保测评标准与实践指南

在信息时代&#xff0c;数据安全与隐私保护成为企业和组织不可忽视的关键议题。等保测评&#xff0c;即信息安全等级保护测评&#xff0c;作为我国信息安全管理体系的重要组成部分&#xff0c;为各行业提供了标准化的安全评估与改进路径。本文旨在深度解读等保测评标准的核心要…

探索Python日期时间的宝藏:`dateutil`库的神秘面纱

文章目录 探索Python日期时间的宝藏&#xff1a;dateutil库的神秘面纱背景&#xff1a;为何选择dateutil&#xff1f;dateutil是什么&#xff1f;如何安装dateutil&#xff1f;简单函数介绍与使用parse函数&#xff1a;智能日期时间解析relativedelta&#xff1a;计算相对日期t…

一个超强的Python机器学习超参优化库

在机器学习模型的训练过程中,选择合适的超参数对模型性能的提升至关重要。超参数优化是指在给定的超参数空间内,找到一组能够使模型表现最佳的超参数组合。虽然有许多方法可以用来进行超参数优化,但在本文中,我们将重点介绍一个强大且易用的库——Optuna。 什么是Optuna?…

顺序表、单链表、顺序栈,链栈的基本运算

目录 顺序表的基本运算 单链表的基本运算 顺序栈的基本运算 链栈的基本运算 线性表的9个基本运算&#xff1a; 栈的6个基本运算&#xff1a; 顺序表的基本运算 //顺序表的基本运算************************************************************** #include<stdio…

通过yfinance获取股票历史数据

以比亚迪为例&#xff0c;要获取A股比亚迪的十年的历史数据并保存为CSV文件&#xff0c;我们可以使用Python中的第三方库如pandas和yfinance。yfinance库是一个用于下载雅虎财经数据的工具&#xff0c;它支持股票、期权等金融工具的数据获取。 1.安装yfinance和pandas 首先&a…

云服务器带宽什么意思?如何正确选择

云服务器带宽什么意思?云服务器带宽指的是云服务器在互联网上的数据传输能力。就像水流通过水管一样&#xff0c;数据通过所谓的“带宽”在网络中流动。这个带宽越大&#xff0c;每秒能够传输的数据就越多&#xff0c;对大量访问处理的能力也就越强。云服务器带宽是云服务器可…