数据比较器,对比数据前后变化细节

news/2025/2/14 5:51:38/

前言

在开发的过程中,有时候需要对数据进行比对,来判断是否发生变化。如果一个字段一个字段比较,就太麻烦了。所以通过整合注解与反射的方式,实现一个通用的实体数据比较框架。

设计

  1. 使用注解,确定需要比较的属性。
  2. 反射获取属性与数据内容。
  3. 循环比较数据内容,并写入到结果中。
  4. 提供多种比较入参

总体结构如下:

正文

1、定义注解

1) 实体注解,确定实体名称

不是基本类型是,必须要有该注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 属性实体标识 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface PropertyEntity {/** 实体唯一标识 */String value();}
复制代码

2) 主键注解,校验数据是否一致

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 唯一标记,可以又多个,用于联合索引 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface PropertyId {
}
复制代码

3) 属性描述注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 属性描述 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyField {/** 中文描述 */String name() default "";/** 排序字段,与@PropertyOrder可以同时使用,取两个最大的为主 */float order() default 0.00F;}
复制代码

4) 顺序注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 属性排序 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyOrder {/** 排序值 */float value() default 0.00F;}
复制代码

5) 排除注解,不进行比较

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 属性忽略比较 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyIgnore {}
复制代码

6) 自定义比较器

如果有特殊比较方式,则自行定义比较器

import com.cah.project.compare.comparator.DefaultComparator;
import com.cah.project.compare.comparator.IComparator;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 功能描述: 属性比较器,可以自定义 <br/>*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyComparator {/** 比较器,默认比较器 */Class<? extends IComparator> compare() default DefaultComparator.class;}
复制代码

2、自定义比较器

1) 比较器接口

/*** 功能描述: 比较器接口 <br/>*/
public interface IComparator<T> {/*** 功能描述: 对象比较 <br/>** @param t1 对象1* @param t2 对象2* @return "int" 返回比较结果 0-相同;非0-不同*/int compare(T t1, T t2);}
复制代码

2) 默认比较器实现

import java.util.Date;
import java.util.Objects;/*** 功能描述: 默认比较器 <br/>*/
public class DefaultComparator implements IComparator<Object> {@Overridepublic int compare(Object o1, Object o2) {// 同时为空,为相同if(Objects.isNull(o1) && Objects.isNull(o2)) {return 0;}// 都不为空if(!Objects.isNull(o2) && !Objects.isNull(o1)) {if(o1 instanceof Date) {return ((Date) o1).compareTo((Date) o2);} else {if(o1 == o2 || o1.equals(o2)) {return 0;}}return -1;}return -1;}
}
复制代码

3、异常类

import com.cah.project.compare.enums.ExceptionEnum;
import lombok.AllArgsConstructor;/*** 功能描述: 比较异常类 <br/>*/
@AllArgsConstructor
public class CompareException extends RuntimeException {private final String code;private final String desc;public CompareException(ExceptionEnum ee) {this(ee.getCode(), ee.getDesc());}public CompareException(ExceptionEnum ee, Object... args) {this(ee.getCode(), String.format(ee.getDesc(), args));}}
复制代码

4、枚举定义

1) 变化类型:新增,修改,删除,无变化等四种情况

import lombok.AllArgsConstructor;
import lombok.Getter;/*** 功能描述: 变化类型枚举 <br/>*/
@Getter
@AllArgsConstructor
public enum ChangeTypeEnum {ADDED("1", "新增"),REMOVED("2", "删除"),MODIFIED("3", "修改"),UNCHANGED("4", "无变化"),;private final String code;private final String desc;}
复制代码

2) 模型类型枚举

import lombok.AllArgsConstructor;
import lombok.Getter;/*** 功能描述: 模型类型 <br/>*/
@Getter
@AllArgsConstructor
public enum ModelTypeEnum {ENTITY("Entity", "实体"),PROPERTY("Property", "基础属性"),ENTITY_PROPERTY("EntityProperty", "实体属性"),LIST_PROPERTY("ListProperty", "列表属性"),MAP_PROPERTY("MapProperty", "Map属性"),;private final String code;private final String desc;
}
复制代码

3) 异常枚举

import lombok.AllArgsConstructor;
import lombok.Getter;/*** 功能描述: 异常枚举 <br/>*/
@Getter
@AllArgsConstructor
public enum ExceptionEnum {OVER_DEPTH("0", "数据结构深度超过指定范围"),INCONSISTENT_CLASS("1", "比较的对象类型不一致"),PROPERTY_ENTITY_NULL("2", "比较的对象必须拥有@PropertyEntity注解"),PROPERTY_ID_NULL("3", "比较的对象必须拥有@PropertyId注解"),PROPERTY_ID_TYPE("4", "对象%s的@PropertyId注解类型必须为String或Long"),PROPERTY_ID_VALUE_NULL("5", "对象%s属性%s的@PropertyId注解的值为空"),;private final String code;private final String desc;
}
复制代码

4) 实体类型枚举

这里使用了枚举+单例的模式。这里为什么不使用策略枚举的原因,在 AnalyzeUtil中需要做属性类型的判断,不方便使用。

import com.cah.project.compare.process.IPropertyProcess;
import com.cah.project.compare.process.impl.BaseTypeProcess;
import com.cah.project.compare.process.impl.EntityTypeProcess;
import com.cah.project.compare.process.impl.ListTypeProcess;
import com.cah.project.compare.process.impl.MapTypeProcess;
import lombok.AllArgsConstructor;
import lombok.Getter;import java.util.List;
import java.util.Map;/*** 功能描述: 实体类型枚举 <br/>*/
@Getter
@AllArgsConstructor
public enum PropertyTypeEnum {BASE_TYPE("base", "基础数据类型(int/String/...)", new BaseTypeProcess()),LIST_TYPE(List.class.getTypeName(), "List", new ListTypeProcess()),MAP_TYPE(Map.class.getTypeName(), "Map", new MapTypeProcess()),ENTITY_OBJECT_TYPE("entityObject", "自定义实体对象", new EntityTypeProcess()),;private final String typeName;private final String desc;// 处理器private final IPropertyProcess process;}
复制代码

5、处理器,与实体类型枚举一起使用

1) 处理器接口

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;/*** 功能描述: 实体过程 <br/>*/
public interface IPropertyProcess {/*** 功能描述: 单个对象处理 <br/>** @param pm 属性模型* @param cte 变化类型* @return "com.cah.project.compare.model.ChangeModel"*/ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException;/*** 功能描述: 两个对象处理 <br/>** @param beforePm before属性模型* @param afterPm after属性模型* @return "com.cah.project.compare.model.ChangeModel"*/ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException;}
复制代码

2) 处理器抽象类

将共有的方法封装在这里,方便各个真实处理器继承使用

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;import java.util.Objects;public abstract class AbsProcess implements IPropertyProcess {protected abstract ModelTypeEnum getModelType();/*** 功能描述: 获取基本的类型 <br/>** @param pm 属性模型* @param cte 变化类型* @return "com.compare.model.ChangeModel"*/protected ChangeModel getBaseChangeModel(PropertyModel pm, ChangeTypeEnum cte) {ChangeModel cm = getBaseChangeModel(pm);cm.setChangeType(cte);if(!Objects.isNull(pm.getValue())) {cm.setTypeValue(pm.getValue().toString());}// 删除的,说明before有,after没有cm.setBefore(ChangeTypeEnum.REMOVED.equals(cte) ? pm.getValue() : null);// 新增的,说明after有,before没有cm.setAfter(ChangeTypeEnum.ADDED.equals(cte) ? pm.getValue() : null);return cm;}/*** 功能描述: 获取基本的类型 <br/>** @param beforePm 改变前属性模型* @param afterPm 改变后属性模型* @return "com.compare.model.ChangeModel"*/protected ChangeModel getBaseChangeModel(PropertyModel beforePm, PropertyModel afterPm) {ChangeModel cm = getBaseChangeModel(beforePm);// 删除的,说明before有,after没有cm.setBefore(beforePm.getValue());// 新增的,说明after有,before没有cm.setAfter(afterPm.getValue());return cm;}/*** 功能描述: 获取基本的类型 <br/>** @param pm 属性模型* @return "com.compare.model.ChangeModel"*/protected ChangeModel getBaseChangeModel(PropertyModel pm) {ChangeModel cm = new ChangeModel();cm.setTypeName(pm.getName());cm.setTypeComment(pm.getPropertyName());cm.setModelType(getModelType());return cm;}/*** 功能描述: 比较对象 <br/>** @param cm 变化模型* @param beforePm before* @param afterPm after*/protected void compareChangeType(ChangeModel cm, PropertyModel beforePm, PropertyModel afterPm) {if(beforePm.getComparator().compare(beforePm.getValue(), afterPm.getValue()) == 0) {cm.setChangeType(ChangeTypeEnum.UNCHANGED);} else {cm.setChangeType(ChangeTypeEnum.MODIFIED);}}}
复制代码

3) 基本类型处理器

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;/*** 功能描述: 基本类型处理 <br/>*/
public class BaseTypeProcess extends AbsProcess {@Overrideprotected ModelTypeEnum getModelType() {return ModelTypeEnum.PROPERTY;}@Overridepublic ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) {return getBaseChangeModel(pm, cte);}@Overridepublic ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) {ChangeModel cm = getBaseChangeModel(beforePm, afterPm);compareChangeType(cm, beforePm, afterPm);return cm;}}
复制代码

4) 实体类型处理器

需要与 CompareHelper配合使用,可能存在实体套实体的情况,会产生递归。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;
import com.cah.project.compare.util.CompareHelper;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;/*** 功能描述: 自定义实体类型处理 <br/>*/
public class EntityTypeProcess extends AbsProcess {@Overrideprotected ModelTypeEnum getModelType() {return ModelTypeEnum.ENTITY_PROPERTY;}@Overridepublic ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(pm, cte);// 设置子节点if(!Objects.isNull(pm.getValue())) {List<ChangeModel> children = new ArrayList<>();ChangeModel child = CompareHelper.assemblyChangeModelObj(pm.getValue(), cte);children.add(child);cm.setChildren(children);}return cm;}@Overridepublic ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(beforePm, afterPm);// 如果两个都为空,则为没变化if(beforePm.getValue() == null && afterPm.getValue() == null) {cm.setChangeType(ChangeTypeEnum.UNCHANGED);}// 如果before的children不为空,after的为空,则为删除if(beforePm.getValue() != null && afterPm.getValue() == null) {cm.setChangeType(ChangeTypeEnum.REMOVED);List<ChangeModel> children = new ArrayList<>();ChangeModel child = CompareHelper.assemblyChangeModelObj(beforePm.getValue(), ChangeTypeEnum.REMOVED);children.add(child);cm.setChildren(children);}// 如果before的children为空,after的不为空,则为新增if(beforePm.getValue() == null && afterPm.getValue() != null) {cm.setChangeType(ChangeTypeEnum.ADDED);List<ChangeModel> children = new ArrayList<>();ChangeModel child = CompareHelper.assemblyChangeModelObj(afterPm.getValue(), ChangeTypeEnum.ADDED);children.add(child);cm.setChildren(children);}// 如果两个都不为空,则重新调用比较if(beforePm.getValue() != null && afterPm.getValue() != null) {if(beforePm.getComparator() != null) {compareChangeType(cm, beforePm, afterPm);} else {// 默认未变化cm.setChangeType(ChangeTypeEnum.UNCHANGED);// 设置子节点List<ChangeModel> children = new ArrayList<>();ChangeModel child = CompareHelper.assemblyChangeModelObj(beforePm.getKey(), beforePm.getValue(), afterPm.getValue());children.add(child);cm.setChildren(children);if(!children.isEmpty()) {// 根据子信息,重新设置变化类型cm.setChangeType(CompareHelper.getChildrenChangeType(children));}}}return cm;}
}
复制代码

5) List处理器

因为List中一般是对象实体,所以需要用到 实体类型处理器,还要使用解析工具AnalyzeUtil,也会产生递归调用。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;
import com.cah.project.compare.util.AnalyzeUtil;
import com.cah.project.compare.util.CompareHelper;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;/*** 功能描述: List类型处理 <br/>*/
public class ListTypeProcess extends AbsProcess {@Overrideprotected ModelTypeEnum getModelType() {return ModelTypeEnum.LIST_PROPERTY;}@Overridepublic ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(pm, cte);if(!Objects.isNull(pm.getValue())) {// 设置子节点List<ChangeModel> children = new ArrayList<>();// 继续比较列表Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) pm.getValue());CompareHelper.assemblyChangeModelList(children,ChangeTypeEnum.REMOVED.equals(cte) ? stringObjectMap : null,ChangeTypeEnum.ADDED.equals(cte) ? stringObjectMap : null);cm.setChildren(children);}return cm;}@Overridepublic ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(beforePm, afterPm);// 如果两个都为空,则为没变化if(beforePm.getValue() == null && afterPm.getValue() == null) {cm.setChangeType(ChangeTypeEnum.UNCHANGED);}// 如果before的children不为空,after的为空,则为删除if(beforePm.getValue() != null && afterPm.getValue() == null) {cm.setChangeType(ChangeTypeEnum.REMOVED);// 设置子节点List<ChangeModel> children = new ArrayList<>();// 继续比较列表Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) beforePm.getValue());CompareHelper.assemblyChangeModelList(children, stringObjectMap, null);cm.setChildren(children);}// 如果before的children为空,after的不为空,则为新增if(beforePm.getValue() == null && afterPm.getValue() != null) {cm.setChangeType(ChangeTypeEnum.ADDED);// 设置子节点List<ChangeModel> children = new ArrayList<>();// 继续比较列表Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) afterPm.getValue());CompareHelper.assemblyChangeModelList(children, null, stringObjectMap);cm.setChildren(children);}// 如果两个都不为空,则重新调用比较if(beforePm.getValue() != null && afterPm.getValue() != null) {// 默认未变化cm.setChangeType(ChangeTypeEnum.UNCHANGED);// 设置子节点List<ChangeModel> children = new ArrayList<>();// 继续比较列表Map<String, Object> beforeObjMap = AnalyzeUtil.toMap((List) beforePm.getValue());Map<String, Object> afterObjMap = AnalyzeUtil.toMap((List) afterPm.getValue());CompareHelper.assemblyChangeModelList(children, beforeObjMap, afterObjMap);cm.setChildren(children);if(!children.isEmpty()) {// 根据子信息,重新设置变化类型cm.setChangeType(CompareHelper.getChildrenChangeType(children));}}return cm;}
}
复制代码

5) Map处理器

如果在设计模型的过程中使用到了Map,要考虑一下,是不是可以使用实体进行定义。所以这个处理器就自由发挥吧,没有开发。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;/*** 功能描述: Map类型处理 <br/>*/
public class MapTypeProcess extends AbsProcess {@Overrideprotected ModelTypeEnum getModelType() {return ModelTypeEnum.MAP_PROPERTY;}@Overridepublic ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(pm, cte);// TODO ...return cm;}@Overridepublic ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {ChangeModel cm = getBaseChangeModel(beforePm, afterPm);// TODO ...return cm;}
}
复制代码

6、模型定义

1) 实体解析模型

解析需要比较的实体,进行存储。

import com.cah.project.compare.comparator.IComparator;
import com.cah.project.compare.enums.PropertyTypeEnum;
import lombok.Data;import java.lang.reflect.Type;/*** 功能描述: 属性模型 <br/>*/
@Data
public class PropertyModel {/** 如果是object,则key,否则与value一致 */private String key;/** 属性名 */private String name;/** 属性值 */private Object value;/** 设置属性描述 */private String propertyName;/** 属性所属的类 */private Class<?> declaring;/** 属性类型 */private Type type;/** 属性类型枚举 */private PropertyTypeEnum pte;/** 属性比较器 */private IComparator comparator;/** 所属实体标识 */private String propertyEntity;/** 排序 */private float order;
}
复制代码

2) 变化模型

最终每条数据变化情况

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import lombok.Data;import java.util.List;/*** 功能描述: 改变模型 <br/>*/
@Data
public class ChangeModel {/** 类型名 */private String typeName;/** 类型值 */private String typeValue;/** 类型描述(如果为对象,则是PropertyEntity,如果是属性,则为PropertyName) */private String typeComment;/** 变化类型 */private ChangeTypeEnum changeType;/** 改变前的数据 */private Object before;/** 改变后的数据 */private Object after;/** 子节点 */private List<ChangeModel> children;/** 模型类型 */private ModelTypeEnum modelType;
}
复制代码

7、解析工具 AnalyzeUtil

对比较对象进行解析的工具类

import com.cah.project.compare.annotation.*;
import com.cah.project.compare.comparator.DefaultComparator;
import com.cah.project.compare.enums.ExceptionEnum;
import com.cah.project.compare.enums.PropertyTypeEnum;
import com.cah.project.compare.exception.CompareException;
import com.cah.project.compare.model.PropertyModel;import java.lang.reflect.Field;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;/*** 功能描述: 解析工具 <br/>*/
public class AnalyzeUtil {/** Key连接符 */private static final String KEY_LINK = "|_|";/*** 功能描述: 将List转换成Map,key为List中对象的 唯一标识(@PropertyId注解) <br/>** @param cs Collection集合* @return "java.util.Map<java.lang.String,java.lang.Object>"*/public static Map<String, Object> toMap(Collection<?> cs) throws IllegalAccessException {Map<String, Object> map = new HashMap<>();if(cs != null && !cs.isEmpty()) {for(Object o : cs) {// 如果是基本类型,则直接toStringString key = isBaseType(o.getClass().getName()) ? o.toString() : getKey(o);map.put(key, o);}}return map;}/*** 功能描述: 将对象转成map,key为属性名,value为对象映射成的PropertyModel模型 <br/>** @param o 对象* @return "java.util.Map<java.lang.String,com.compare.model.PropertyModel>"*/public static Map<String, PropertyModel> toMap(Object o) throws IllegalAccessException, InstantiationException {if(Objects.isNull(o)) {return null;}// 获取全部字段Field[] fields = o.getClass().getDeclaredFields();List<PropertyModel> list = new ArrayList<>(fields.length);String propertyEntity = getPropertyEntity(o);String key = getKey(o);for (Field field : fields) {// 非忽略注解if(!isIgnore(field)) {field.setAccessible(true);PropertyModel pm = new PropertyModel();pm.setKey(key);// 设置属性信息pm.setName(field.getName());pm.setValue(field.get(o));// 设置类型pm.setType(field.getType());// 设置属性类型枚举if(isBaseType(pm.getType().getTypeName())) {pm.setPte(PropertyTypeEnum.BASE_TYPE);} else if(isList(pm.getType().getTypeName())) {pm.setPte(PropertyTypeEnum.LIST_TYPE);} else if(isMap(pm.getType().getTypeName())) {pm.setPte(PropertyTypeEnum.MAP_TYPE);} else {pm.setPte(PropertyTypeEnum.ENTITY_OBJECT_TYPE);}PropertyField propertyField = field.getAnnotation(PropertyField.class);if(propertyField != null) {pm.setPropertyName(propertyField.name());pm.setOrder(propertyField.order());}PropertyOrder propertyOrder = field.getAnnotation(PropertyOrder.class);if(propertyOrder != null) {// 两个注解都有,哪个大取哪个pm.setOrder(Math.max(pm.getOrder(), propertyOrder.value()));}// 设置比较器PropertyComparator pc = field.getAnnotation(PropertyComparator.class);if(pc != null) {pm.setComparator(pc.compare().newInstance());} else {if(PropertyTypeEnum.BASE_TYPE.equals(pm.getPte())) {pm.setComparator(new DefaultComparator());}}pm.setDeclaring(field.getDeclaringClass());pm.setPropertyEntity(propertyEntity);list.add(pm);}}// 转成有序mapreturn list.stream().sorted(Comparator.comparing(PropertyModel::getOrder)).collect(Collectors.toMap(PropertyModel::getName, e -> e, throwingMerger(), LinkedHashMap::new));}/*** 功能描述: 异常处理 <br/>** @return "java.util.function.BinaryOperator<T>"*/private static <T> BinaryOperator<T> throwingMerger() {return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };}/*** 功能描述: 确定唯一标识数据 <br/>** @param o 对象* @return "java.lang.String"*/public static String getKey(Object o) throws IllegalAccessException {if(isBaseType(o.getClass().getName())) {return o.toString();}// 获取全部字段Field[] fields = o.getClass().getDeclaredFields();StringBuilder sb = new StringBuilder();for(Field field : fields) {// 非忽略注解if(!isIgnore(field)) {// 获取@PropertyId注解PropertyId id = field.getAnnotation(PropertyId.class);if(id != null) {// 校验是否基本类型String typeName = field.getType().getTypeName();checkPropertyIdType(typeName, o);field.setAccessible(true);if(field.get(o) == null || "".equals(field.get(o))) {throw new CompareException(ExceptionEnum.PROPERTY_ID_VALUE_NULL, o.getClass().getName(), field.getName());}sb.append(field.get(o).toString());sb.append(KEY_LINK);}}}if(sb.length() <= 0) {throw new CompareException(ExceptionEnum.PROPERTY_ID_NULL);}return sb.substring(0, sb.lastIndexOf(KEY_LINK));}/*** 功能描述: 获取实体标识注解内容 <br/>** @param o 对象* @return "java.lang.String"*/public static String getPropertyEntity(Object o) {return getPropertyEntity(o.getClass());}/*** 功能描述: 获取实体标识注解内容 <br/>** @param clazz 类* @return "java.lang.String"*/public static String getPropertyEntity(Class<?> clazz) {if(isBaseType(clazz.getName())) {return "";}PropertyEntity pe = clazz.getAnnotation(PropertyEntity.class);if(pe == null) {// 不为基本类型时,必须要有PropertyEntity注解throw new CompareException(ExceptionEnum.PROPERTY_ENTITY_NULL);}return pe.value();}/*** 功能描述: 是忽略注解字段 <br/>** @param field 字段* @return "boolean" true-是;false-不是*/private static boolean isIgnore(Field field) {return field.getAnnotation(PropertyIgnore.class) != null;}/*** 功能描述: 是否列表 <br/>* * @param typeName 类型名称* @return "boolean"*/private static boolean isList(String typeName) {return List.class.getTypeName().equals(typeName);}/*** 功能描述: 是否Map <br/>** @param typeName 类型名称* @return "boolean"*/private static boolean isMap(String typeName) {return Map.class.getTypeName().equals(typeName);}/*** 功能描述: 是否基本类型 <br/>** @param className 类* @return "boolean" true-是;false-不是*/private static boolean isBaseType(String className) {if(className.equals(Integer.class.getName()) ||className.equals(int.class.getName()) ||className.equals(Byte.class.getName()) ||className.equals(byte.class.getName()) ||className.equals(Long.class.getName()) ||className.equals(long.class.getName()) ||className.equals(Double.class.getName()) ||className.equals(double.class.getName()) ||className.equals(Float.class.getName()) ||className.equals(float.class.getName()) ||className.equals(Character.class.getName()) ||className.equals(char.class.getName()) ||className.equals(Short.class.getName()) ||className.equals(short.class.getName()) ||className.equals(java.math.BigDecimal.class.getName()) ||className.equals(java.math.BigInteger.class.getName()) ||className.equals(Boolean.class.getName()) ||className.equals(boolean.class.getName()) ||className.equals(String.class.getName())) {return true;}return false;}/*** 功能描述: 基本类型校验 <br/>*/private static void checkPropertyIdType(String typeName, Object o) {if(!"java.lang.String".equals(typeName)&& !"java.lang.Long".equals(typeName)&& !"java.lang.Integer".equals(typeName)&& !"long".equals(typeName)&& !"int".equals(typeName)) {throw new CompareException(ExceptionEnum.PROPERTY_ID_TYPE, o.getClass().getName());}}
}
复制代码

8、数据比较核心类

唯一提供对外的方法为 compareProcess,接收两个数据列表。然后对数据进行比较,返回

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;import java.util.*;
import java.util.stream.Collectors;/*** 功能描述: 比较帮助类 <br/>*/
public class CompareHelper {/*** 功能描述: 数据比较(提供给外部使用) <br/>*/public static void compareProcess(List<ChangeModel> changeList, Collection<?> before, Collection<?> after) throws IllegalAccessException, InstantiationException {if((before == null || before.isEmpty()) && (after == null || after.isEmpty())) {return;}Map<String, Object> afterObjMap = null, beforeObjMap = null;if(before == null || before.isEmpty()) {// 全部为新增// 转换为Map<String, Object> key:主键值,value:对象信息afterObjMap = AnalyzeUtil.toMap(after);}if(after == null || after.isEmpty()) {// 全部为删除beforeObjMap = AnalyzeUtil.toMap(before);}if(before != null && !before.isEmpty() && after != null && !after.isEmpty()) {// 修改或者不变beforeObjMap = AnalyzeUtil.toMap(before);afterObjMap = AnalyzeUtil.toMap(after);}// 组转成changeModelassemblyChangeModelList(changeList, beforeObjMap, afterObjMap);}/*** 功能描述: 组装 <br/>** @param changeList 变化列表* @param beforeObjMap before* @param afterObjMap after*/public static void assemblyChangeModelList(List<ChangeModel> changeList,Map<String, Object> beforeObjMap,Map<String, Object> afterObjMap) throws IllegalAccessException, InstantiationException {if(beforeObjMap == null) {Set<Map.Entry<String, Object>> entries = afterObjMap.entrySet();for (Map.Entry<String, Object> entry : entries) {ChangeModel cm = assemblyChangeModelObj(entry.getValue(), ChangeTypeEnum.ADDED);changeList.add(cm);}return;}if(afterObjMap == null) {Set<Map.Entry<String, Object>> entries = beforeObjMap.entrySet();for (Map.Entry<String, Object> entry : entries) {ChangeModel cm = assemblyChangeModelObj(entry.getValue(), ChangeTypeEnum.REMOVED);changeList.add(cm);}return;}Set<Map.Entry<String, Object>> beforeEntries = beforeObjMap.entrySet();for(Map.Entry<String, Object> beforeEntry : beforeEntries) {ChangeModel cm;if (!afterObjMap.containsKey(beforeEntry.getKey())) {// 如果在after中不存在key,则为删除cm = assemblyChangeModelObj(beforeEntry.getValue(), ChangeTypeEnum.REMOVED);} else {// 继续判断是不变,还是修改cm = assemblyChangeModelObj(beforeEntry.getKey(), beforeEntry.getValue(), afterObjMap.get(beforeEntry.getKey()));}changeList.add(cm);}// 如果after中有before没有的值,则为新增Set<Map.Entry<String, Object>> afterEntries = afterObjMap.entrySet();for (Map.Entry<String, Object> afterEntry : afterEntries) {if(!beforeObjMap.containsKey(afterEntry.getKey())) {ChangeModel cm = assemblyChangeModelObj(afterEntry.getValue(), ChangeTypeEnum.ADDED);changeList.add(cm);}}}/*** 功能描述: 组转对象 <br/>** @param key 改变前* @param before 改变前* @param after 改变后* @return "com.compare.model.ChangeModel"*/public static ChangeModel assemblyChangeModelObj(String key, Object before, Object after) throws IllegalAccessException, InstantiationException {ChangeModel cm = new ChangeModel();// 默认不变cm.setChangeType(ChangeTypeEnum.UNCHANGED);cm.setModelType(ModelTypeEnum.ENTITY);cm.setBefore(before);cm.setAfter(after);// 设置数据主键cm.setTypeValue(key);// 设置类名cm.setTypeName(before.getClass().getSimpleName());cm.setTypeComment(AnalyzeUtil.getPropertyEntity(before));// 转换属性Map<String, PropertyModel> beforeFiledMap = AnalyzeUtil.toMap(before);Map<String, PropertyModel> afterFiledMap = AnalyzeUtil.toMap(after);if(beforeFiledMap != null && afterFiledMap != null) {// 比较属性是否一致List<ChangeModel> children = new ArrayList<>();// 同一个对象下面,字段属性肯定一致,缩编去一个key就行Set<Map.Entry<String, PropertyModel>> beforeEntries = beforeFiledMap.entrySet();for (Map.Entry<String, PropertyModel> beforeEntry : beforeEntries) {// 获取属性keyString propertyKey = beforeEntry.getKey();// 通过属性类型,对比两个数据ChangeModel child = beforeEntry.getValue().getPte().getProcess().process(beforeEntry.getValue(), afterFiledMap.get(propertyKey));children.add(child);}cm.setChildren(children);}if(cm.getChildren() != null && !cm.getChildren().isEmpty()) {cm.setChangeType(getChildrenChangeType(cm.getChildren()));}return cm;}/*** 功能描述: 获取子属性的最终变化类型 <br/>** @param children 子变化模型* @return "com.compare.enums.ChangeTypeEnum"*/public static ChangeTypeEnum getChildrenChangeType(List<ChangeModel> children) {// 获取children中是否全部为的变化类型List<ChangeTypeEnum> changeTypes = children.stream().map(ChangeModel::getChangeType).distinct().collect(Collectors.toList());if(changeTypes.size() == 1 && ChangeTypeEnum.UNCHANGED.equals(changeTypes.get(0))) {return ChangeTypeEnum.UNCHANGED;} else {return ChangeTypeEnum.MODIFIED;}}/*** 功能描述: 组装单个对象 <br/>** @param obj 对象* @param cte 变化类型* @return "com.compare.model.ChangeModel"*/public static ChangeModel assemblyChangeModelObj(Object obj, ChangeTypeEnum cte) throws IllegalAccessException, InstantiationException {ChangeModel cm = new ChangeModel();// 设置变化类型cm.setChangeType(cte);cm.setModelType(ModelTypeEnum.ENTITY);// 删除的,说明before有,after没有cm.setBefore(ChangeTypeEnum.REMOVED.equals(cte) ? obj : null);// 新增的,说明after有,before没有cm.setAfter(ChangeTypeEnum.ADDED.equals(cte) ? obj : null);// 设置数据主键cm.setTypeValue(AnalyzeUtil.getKey(obj));// 设置类名cm.setTypeName(obj.getClass().getSimpleName());cm.setTypeComment(AnalyzeUtil.getPropertyEntity(obj));// 转换属性Map<String, PropertyModel> filedMap = AnalyzeUtil.toMap(obj);if(filedMap != null) {List<ChangeModel> children = new ArrayList<>();Set<Map.Entry<String, PropertyModel>> entries = filedMap.entrySet();for (Map.Entry<String, PropertyModel> entry : entries) {// 拼接字段ChangeModel cmc = entry.getValue().getPte().getProcess().process(entry.getValue(), cte);if(cmc != null) {children.add(cmc);}}cm.setChildren(children);}return cm;}
}
复制代码

9、提供对外调用类 CompareCore

所有需要对数据进行比较的,都调用该类的方法。

import com.cah.project.compare.enums.ExceptionEnum;
import com.cah.project.compare.exception.CompareException;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.util.CompareHelper;import java.util.*;/*** 功能描述: 比较核心方法 <br/>*/
public class CompareCore {/*** 功能描述: 列表比较 <br/>** @param before 之前* @param after 之后*/public static <E> List<ChangeModel> compare(Collection<E> before, Collection<E> after) throws IllegalAccessException, InstantiationException {List<ChangeModel> changeList = new ArrayList<>();CompareHelper.compareProcess(changeList, before, after);return changeList;}/*** 功能描述: 对象比较 <br/>** @param before 之前* @param after 之后* @return "java.util.List<com.cah.project.compare.model.ChangeModel>"*/public static List<ChangeModel> compare(Object before, Object after) throws IllegalAccessException, InstantiationException {// 校验 o1 和 o2 的类型是一致的if(!before.getClass().equals(after.getClass())) {throw new CompareException(ExceptionEnum.INCONSISTENT_CLASS);}return compare(Collections.singletonList(before), Collections.singletonList(after));}}
复制代码

测试

import com.cah.project.compare.CompareCore;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.test.entity.User;import java.util.ArrayList;
import java.util.List;public class CompareTest {public static void main(String[] args) throws Exception {List<ChangeModel> compare = CompareCore.compare(getUserListLeft().get(1), getUserListRight().get(1));System.out.println(compare);}private static List<User> getUserListLeft() {List<User> list = new ArrayList<>();User user = new User();user.setIdCard("11111");user.setName("张三");list.add(user);User user1 = new User();user1.setIdCard("11112");user1.setName("李四");User user11 = new User();user11.setIdCard("1122");user11.setName("利佩欧");user1.setSpouse(user11);List<User> children = new ArrayList<>();User user111 = new User();user111.setIdCard("11221");user111.setName("利斯海1");children.add(user111);user1.setChildren(children);User user112 = new User();user112.setIdCard("11222");user112.setName("利斯海2");children.add(user112);user1.setChildren(children);list.add(user1);return list;}private static List<User> getUserListRight() {List<User> list = new ArrayList<>();User user = new User();user.setIdCard("11111");user.setName("张三");list.add(user);User user1 = new User();user1.setIdCard("11112");user1.setName("李四");User user11 = new User();user11.setIdCard("1122");user11.setName("利佩欧");user1.setSpouse(user11);List<User> children = new ArrayList<>();User user111 = new User();user111.setIdCard("11221");user111.setName("利斯海1");children.add(user111);user1.setChildren(children);User user112 = new User();user112.setIdCard("11222");user112.setName("利斯海22");children.add(user112);user1.setChildren(children);list.add(user1);return list;}
复制代码

结果

可以看出,只要子项有变化,主项的最终结论也是变化。

[ChangeModel(typeName=User, typeValue=11112, typeComment=用户信息, changeType=MODIFIED, before=User(idCard=11112, name=李四, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)]), after=User(idCard=11112, name=李四, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)]), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11112, after=11112, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=李四, after=李四, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=User, typeValue=11112, typeComment=用户信息, changeType=UNCHANGED, before=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=1122, after=1122, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=利佩欧, after=利佩欧, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY)], modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=MODIFIED, before=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)], after=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)], children=[ChangeModel(typeName=User, typeValue=11221, typeComment=用户信息, changeType=UNCHANGED, before=User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11221, after=11221, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=利斯海1, after=利斯海1, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY), ChangeModel(typeName=User, typeValue=11222, typeComment=用户信息, changeType=MODIFIED, before=User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11222, after=11222, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=MODIFIED, before=利斯海2, after=利斯海22, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY)], modelType=LIST_PROPERTY)], modelType=ENTITY)]复制代码

代码地址

entity-compare-api

总结

项目开发过程中,用到的一个小工具,性能问题,有待优化。后续看看能不能再精简一下配置内容与命名。


http://www.ppmy.cn/news/2273.html

相关文章

单片机硬件和软件延时是啥意思?

软件延时和硬件延时是啥意思&#xff1f;做项目时他俩有什么区别&#xff1f; 今天就来讲讲关于硬件延时和软件延时的内容&#xff0c;以及它们的区别。 硬件和软件延时 延时的种类很多&#xff0c;先给大家普及一下延时相关概念和分类。 1.硬件延时 指利用具有计数功能的…

基于jsp+mysql+ssm学生网上请假系统-计算机毕业设计

项目介绍 随着高校招生规模的逐步扩大和教学方式的改革&#xff0c;在校学生人数将不断增加。另一方面&#xff0c;我国高等学校基层学生考核工作的内容杂&#xff0c;管理细&#xff0c;要求高&#xff0c;头绪多&#xff0c;传统的手工档案式管理办法已基本不适应新形势的要…

html实训大作业《基于HTML+CSS+JavaScript红色文化传媒网站(20页)》

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

最优化方法——QR分解

目录 系列文章目录 一、问题 二、实验思路综述 1.实验工具及算法 2.实验数据 3.实验目标 4.实验步骤 三、相关线性代数知识导入 1.线性无关与基 2.标准正交 3.Gram-Schmidt(正交化)算法 四、QR分解 1.Gram-Schmidt QR 1.1 算法原理 1.2 算法流程 1.3 复杂度分析…

Leetcode682:棒球比赛

原文链接&#xff1a;682. 棒球比赛 - 力扣&#xff08;LeetCode&#xff09; 题目 你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成&#xff0c;过去几回合的得分可能会影响以后几回合的得分。 比赛开始时&#xff0c;记录是空白的。你会得到一个记录操作的…

2022最后一个月,我们该如何学Java​?

2022最后一个月&#xff0c;我们该如何学Java&#xff1f; 互联网的快速发展和激烈竞争&#xff0c;在世界编程语言排行榜中&#xff0c;Java位列前三&#xff0c;占全球编程市场份额的12%左右,各大公司对Java工程师的需求量都很大&#xff0c;要求也越来越高&#xff0c;优秀…

《MongoDB》Mongo Shell中的基本操作-删除操作一览

前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e; 主页: oliver尹的主页 格言: 跌倒了爬起来就好&#xff5e; 来个关注吧&#xff0c;点个赞…

Docker: 小白之路九(从0搭建自己的Docker环境centos7)

Docker环境配置centos7版本 一. 配置对应的docker环境和nvidia-docker(注意加速镜像设置) 二. 环境配置 1. 拉取对应的镜像 docker pull nvidia/cuda:10.2-cudnn7-devel-centos72. 创建新版本的容器并搭建对应的环境 docker run -it --name felaim_sever_centos7 nvidia/c…