利用反射,根据字段合并两个list

/ 后端 / 没有评论 / 175浏览

起因

闲的没事.....

作用

mysql查询如果过多使用join联表查询,则会造成一定压力,影响响应速度;mysql更适合的是直接根据主键或索引简单的取出数据,然后在应用中进行merge合并; 一般这么做:

   List list1 = //查出列表1;
   List list2 = //根据list1的id查出列表2;
   //然后将list2根据list1的id,取出值填充到list1;
   //使用双重循环或者将list2转为map,空间换时间提高速度

写的一个小工具类

package com.wfcm.utils;

import com.wfcm.config.exception.RRException;
import com.wfcm.entity.androidTV.WfAndroidTvIcon;
import com.wfcm.entity.applet.vo.WfAppletManagerPlanListVO;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class DataMergeUtil {

    private DataMergeUtil() {

    }

    /**
     * 处理合并
     * @param mergedDataBuilder 被合并对象
     * @param mergeDataBuilder  合并对象
     */
    public static void merge(@NotNull MergeDataBuilder mergedDataBuilder, @NotNull MergeDataBuilder mergeDataBuilder) {
        WfAssert.isTrue(mergedDataBuilder != null, "mergedDataBuilder不能为空");
        WfAssert.isTrue(mergeDataBuilder != null, "mergeDataBuilder不能为空");

        //将合并列表转换为map,此处没有直接使用字段作为map的value,是因为如果该字段为null,则会
        //报空指针错误,流式处理底层merge判断了value是否为空
        Map mergeMap = (Map) mergeDataBuilder.getList().stream().collect(Collectors.toMap(mergeDataBuilder.primaryKey, Function.identity(), (oldVal, currVal) -> oldVal));

        //获取反射字段,可优化缓存
        Field mergedPrimaryKeyField = null;
        Field mergedKeyField = null;
        Field mergeKeyField = null;
        try {
            Class<?> mergedClass = mergedDataBuilder.list.get(0).getClass();
            mergedPrimaryKeyField = mergedClass.getDeclaredField(getName(mergedDataBuilder.primaryKey));
            mergedKeyField = mergedClass.getDeclaredField(getName(mergedDataBuilder.mergeKey));
            mergeKeyField = mergeDataBuilder.list.get(0).getClass().getDeclaredField(getName(mergeDataBuilder.mergeKey));
        } catch (NoSuchFieldException e) {
            throw new RRException("反射获取变量字段错误", e);
        }
        mergedPrimaryKeyField.setAccessible(true);
        mergedKeyField.setAccessible(true);
        mergeKeyField.setAccessible(true);
      
        //循环填字段变量值
        for (Object mergedItem : mergedDataBuilder.list) {
            try {
                Object mergedPrimaryKey = mergedPrimaryKeyField.get(mergedItem);

                Object mergeData = mergeMap.get(mergedPrimaryKey);
                if (mergeData == null) {
                    return;
                }
                Object mergeKeyData = mergeKeyField.get(mergeData);
                if (mergeKeyData == null) {
                    return;
                }
                mergedKeyField.set(mergedItem, mergeKeyData);
            } catch (IllegalAccessException e) {
                throw new RRException("反射操作字段变量值错误", e);
            }
        }
    }
    
    //需要合并的对象包装
    @Getter
    private static class MergeDataBuilder<T, R> {
        private MergeDataBuilder() {
        }

        private List<T> list;
        private WfFunction<T, R> primaryKey;
        private WfFunction<T, R> mergeKey;

        public static <T, R> MergeDataBuilder<T, R> build(@NotNull List<T> list, @NotNull WfFunction<T, R> primaryKey, @NotNull WfFunction<T, R> mergeKey) {
            WfAssert.isTrue(!CollectionUtils.isEmpty(list), "list不能为空");
            WfAssert.isTrue(primaryKey != null, "primaryKey不能为空");
            WfAssert.isTrue(mergeKey != null, "mergeKey不能为空");

            MergeDataBuilder mergeDataBuilder = new MergeDataBuilder();
            mergeDataBuilder.list = list;
            mergeDataBuilder.mergeKey = mergeKey;
            mergeDataBuilder.primaryKey = primaryKey;
            return mergeDataBuilder;
        }
    }

    //函数式接口
    @FunctionalInterface
    public interface WfFunction<T, R> extends Function<T, R>, Serializable {
    }

    
    //获取函数字段名
    public static <T> String getName(WfFunction<T, ?> fn) {
        // 从function取出序列化方法
        Method writeReplaceMethod;
        try {
            writeReplaceMethod = fn.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        // 从序列化方法取出序列化的lambda信息
        boolean isAccessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda;
        try {
            serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(fn);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        writeReplaceMethod.setAccessible(isAccessible);

        // 从lambda信息取出method、field、class等
        String fieldName = serializedLambda.getImplMethodName().substring("get".length());
        return fieldName = fieldName.replaceFirst(fieldName.charAt(0) + "", (fieldName.charAt(0) + "").toLowerCase());
    }
}

测试:

 public static void main(String[] args) {
        WfAndroidTvIcon icon1 = new WfAndroidTvIcon();
        WfAndroidTvIcon icon2 = new WfAndroidTvIcon();
        WfAndroidTvIcon icon3 = new WfAndroidTvIcon();
        icon1.setId(1);
        icon2.setId(2);
        icon3.setId(3);

        WfAppletManagerPlanListVO vo1 = new WfAppletManagerPlanListVO();
        WfAppletManagerPlanListVO vo2 = new WfAppletManagerPlanListVO();
        WfAppletManagerPlanListVO vo3 = new WfAppletManagerPlanListVO();
        vo1.setCourseId(1);
        vo1.setCourseName("中");
        vo2.setCourseId(2);
        vo2.setCourseName("国");
        vo3.setCourseId(3);
        vo3.setCourseName("人");

        List<WfAndroidTvIcon> list1 = Arrays.asList(icon1, icon2, icon3);
        List<WfAppletManagerPlanListVO> list2 = Arrays.asList(vo1, vo2, vo3);

        DataMergeUtil.merge(MergeDataBuilder.build(list1, WfAndroidTvIcon::getId, WfAndroidTvIcon::getTitle),
                MergeDataBuilder.build(list2, WfAppletManagerPlanListVO::getCourseId, WfAppletManagerPlanListVO::getCourseName));

        list1.stream().forEach(l -> {
            System.out.println(l.getId() + " " + l.getTitle());
        });
    }

输出:

1 中
2 国
3 人