JAVA的嵌套数组DIFF算法

大家好,我是wdm, 针对上一篇一层List的DIFF
https://blog.csdn.net/wudaiming/article/details/131500016?spm=1001.2014.3001.5501

这里我在项目组开发是碰到了一种情况,就是嵌套数组是否相等的比较,项目中我通过重写数组对象的equals方法和set实现了判断是否有改动【包括、增、删、改都属于一种状态返回true,另一种就是啥没改返回false】,写完了我想起上篇文章可以优化用到这。

对于嵌套数组的DIFF算法其实可以在比较器中通过反射获取对象属性的Class类型,如果是List或者Arrays就走数组的判断相等逻辑【可以转换成set去重写数组对象的equals方法,最后set1.equals(set2)来判断】,否则就走Java基本类型的equals。

对于实现,其实只需要改动比较器:

package com.wdm.util;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.List;

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 (value1 != null && value2 != null && value1.getClass().isArray() && value2.getClass().isArray()) {
                    if (!isArraysEqual(value1, value2)) {
                        return compareArrays(value1, value2);
                    }
                } else if (value1 instanceof List && value2 instanceof List) {
                    if (!isListEqual((List<?>) value1, (List<?>) value2)) {
                        return compareLists((List<?>) value1, (List<?>) value2);
                    }
                } else if (!isEqual(value1, value2)) {
                    return compareValues(value1, value2);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return 0;
    }

    private boolean isArraysEqual(Object arr1, Object arr2) {
        int length1 = Array.getLength(arr1);
        int length2 = Array.getLength(arr2);
        if (length1 != length2) {
            return false;
        }
        for (int i = 0; i < length1; i++) {
            Object element1 = Array.get(arr1, i);
            Object element2 = Array.get(arr2, i);
            if (!isEqual(element1, element2)) {
                return false;
            }
        }
        return true;
    }

    private boolean isListEqual(List<?> list1, List<?> list2) {
        int size1 = list1.size();
        int size2 = list2.size();
        if (size1 != size2) {
            return false;
        }
        for (int i = 0; i < size1; i++) {
            Object element1 = list1.get(i);
            Object element2 = list2.get(i);
            if (!isEqual(element1, element2)) {
                return false;
            }
        }
        return true;
    }

    private boolean isEqual(Object obj1, Object obj2) {
        if (obj1 == null && obj2 == null) {
            return true;
        }
        if (obj1 == null || obj2 == null) {
            return false;
        }
        return obj1.equals(obj2);
    }

    private int compareArrays(Object arr1, Object arr2) {
        int length1 = Array.getLength(arr1);
        int length2 = Array.getLength(arr2);
        int minSize = Math.min(length1, length2);
        for (int i = 0; i < minSize; i++) {
            Object element1 = Array.get(arr1, i);
            Object element2 = Array.get(arr2, i);
            if (!isEqual(element1, element2)) {
                return compareValues(element1, element2);
            }
        }
        return Integer.compare(length1, length2);
    }

    private int compareLists(List<?> list1, List<?> list2) {
        int size1 = list1.size();
        int size2 = list2.size();
        int minSize = Math.min(size1, size2);
        for (int i = 0; i < minSize; i++) {
            Object element1 = list1.get(i);
            Object element2 = list2.get(i);
            if (!isEqual(element1, element2)) {
                return compareValues(element1, element2);
            }
        }
        return Integer.compare(size1, size2);
    }

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

然后对于DiffUtil在上篇文章的错误进行修复后的代码如下:

package com.wdm.util;

import cn.hutool.core.collection.CollUtil;
import com.wdm.enumm.EnumType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.sql.Wrapper;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author: wudaiming
 * @date: 2023/6/29
 * @version: 1.0
 * @description:
 */
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>> addedList;
        /**
         * 修改后的对象列表
         */
        private List<TargetWrapper<T>> changedList;
        /**
         * 已删除对象列表
         */
        private List<TargetWrapper<T>> deletedList;
    }

    /**
     * 对比两个List的元素
     * 

* 如果 baseList 的元素在 targetList 中存在 PrimaryKey 相等的元素并且 elementComparator 比较结果不相等,则将修改后的值添加到changedList列表中; * 如果 baseList 的元素在 targetList 中不存在,将baseList中的元素添加到deletedList中; * 如果 targetList 的元素在 baseList 中不存在,将targetList中的元素添加到addedList中; *

* complexity: O(n) * * @param baseList 基础List(原来的List) * @param targetList 目标List(最新的List) * @param elementComparator 元素比较器 *primaryKeyExtractor * @param * @return 对比结果 */ public static <T> DiffResult<T> diffList(List<T> baseList, List<T> targetList, @NotNull Function<T, Object> primaryKeyExtractor, @NotNull ObjectComparator<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<>(); //找出新增的 和需要更新的 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)); } } return new DiffResult<T>() .setAddedList(addedList) .setChangedList(changedList) .setDeletedList(deletedList); } 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 * @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.ADD)).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; private List<User> sonUserList; } @Data @AllArgsConstructor public static class SonUser { private Integer id; private String userName; private String address; private String email; } }

package com.wdm;

import com.wdm.util.DiffListUtil;
import com.wdm.util.ObjectComparator;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/**
 * @author: wudaiming
 * @date: 2023/6/29
 * @version: 1.0
 * @description:
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class DiffListUtilApplicationTest {
    @Test
    public void test1() {
        List<DiffListUtil.User> userList = new ArrayList<>();
        DiffListUtil diffListUtil = new DiffListUtil();
        DiffListUtil.User sonUser = new DiffListUtil.User(55, "John", "hunan", "[email protected]", new ArrayList<>());
        DiffListUtil.User sonUser11 = new DiffListUtil.User(11, "John", "hunan", "[email protected]", new ArrayList<>());
        DiffListUtil.User sonUser12 = new DiffListUtil.User(11, "John1", "hunan", "[email protected]", new ArrayList<>());

        userList.add(new DiffListUtil.User(11,"John","hunan","[email protected]", new ArrayList<>(){{
            add(sonUser);
            //add(sonUser11);
        }}));
        userList.add(new DiffListUtil.User(22,"Tom","jilin","[email protected]", new ArrayList<>(){{
            add(sonUser);
            add(sonUser11);
        }}));
        List<DiffListUtil.User> userListAfter = new ArrayList<>();

        userListAfter.add(new DiffListUtil.User(11,"John","hunan","[email protected]", new ArrayList<>(){{
            add(sonUser);
            //add(sonUser11);
        }}));
        userListAfter.add(new DiffListUtil.User(22,"Tom","jilin","[email protected]", new ArrayList<>(){{
            add(sonUser);
            //add(sonUser11);
        }}));
        Function<DiffListUtil.User, Object> primaryKeyExtractor = user -> user.getId();

        ObjectComparator<DiffListUtil.User> userComparator = new ObjectComparator<>();
        DiffListUtil.DiffResult<DiffListUtil.User> userDiffResult = diffListUtil.diffList(userListAfter, userList, primaryKeyExtractor, userComparator);
        //unionSubIntersection(userList, userListAfter);
        System.out.println(userDiffResult);
    }

    private void unionSubIntersection(List<DiffListUtil.User> userList, List<DiffListUtil.User> userListAfter) {
        /**
         * @description 求两个集合的不同元素
         * @author wudaiming
         * @date 13:29 2023/7/20
         */
        List<DiffListUtil.User> union = (List<DiffListUtil.User>) CollectionUtils.union(userListAfter, userList);
        List<DiffListUtil.User> intersection = (List<DiffListUtil.User>) CollectionUtils.intersection(userListAfter, userList);

        List<DiffListUtil.User> difference = findDifference(union, intersection);
    }

    public List<DiffListUtil.User> findDifference(List<DiffListUtil.User> list1, List<DiffListUtil.User> list2) {
        return (List<DiffListUtil.User>) CollectionUtils.subtract(list1, list2);
    }
}

总结:

最后,其实这个实现我还是只以第一层为判断ADD,MODIFIED,DELETED的主体,也就是说对于嵌套数组中如果对象元素有任何增、删、改都看作第一层的MODIFIED,如果有朋友想要精确到嵌套数组的ADD,MODIFIED,DELETED可以留言讨论!!这个目前对我够用了。

感谢大家看完我的文章,如果大家喜欢我会继续更新项目中遇到的小东西,搞技术就是要不断积累,大家共勉!!!!!

你可能感兴趣的:(JAVA常用工具类,java,算法)