常用List工具类(取交集、并集等等)

支持操作:

  • 根据指定字段,获取两个对象集合的交集、补集、并集等
  • 将对象中的多个字段值,抽取到一个List中
java 复制代码
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Description:List工具类。用于处理两个列表的交集、并集、差集等操作。
 *
 * @author jiangniao
 * @date 2024-3-22
 */
public class ListUtil {

    private ListUtil() {
    }

    /**
     * 提取多个字段的值到一个list
     *
     * @param list      对象集合
     * @param fieldName 需要提取的字段名
     * @return
     */
    public static List<String> extractFieldsToList(List<?> list, String... fieldName) {
        return list.stream().flatMap(item->Arrays.stream(fieldName).map(field->BeanUtils.getSimpleProperty(item, field))).collect(Collectors.toList());
    }

    /**
     * 根据指定字段找出两个列表的交集。
     *
     * @param list1
     * @param list2
     * @param fields
     * @param <T>
     * @return
     */
    public static <T> List<T> intersection(List<T> list1, List<T> list2, String... fields) {
        return list1.stream().filter(item1->containsWithFields(list2, item1, fields)).collect(Collectors.toList());
    }

    /**
     * 根据指定字段找出两个列表的并集
     *
     * @param list1
     * @param list2
     * @param fields
     * @param <T>
     * @return
     */
    public static <T> List<T> union(List<T> list1, List<T> list2, String... fields) {
        List<T> union = new ArrayList<>(list1);
        union.addAll(list2.stream().filter(item->!containsWithFields(list1, item, fields)).collect(Collectors.toList()));
        return union;
    }

    /**
     * 根据指定字段找出两个列表的差集。list1中有,list2中没有的元素
     *
     * @param list1
     * @param list2
     * @param fields
     * @param <T>
     * @return 返回list1过滤后的数据
     */
    public static <T> List<T> difference(List<T> list1, List<T> list2, String... fields) {
        return list1.stream().filter(item->!containsWithFields(list2, item, fields)).collect(Collectors.toList());
    }

    /**
     * 找出两个列表的差集。list1中有,list2中没有的元素。
     * <p>用于非Object对象的比较,比如String、Integer等
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return 返回list1过滤后的数据
     */
    public static <T> List<T> difference(List<T> list1, List<T> list2) {
        list1.removeAll(list2);
        return list1;
    }

    /**
     * 辅助方法,判断列表中是否包含指定对象
     *
     * @param list
     * @param item
     * @param fields
     * @param <T>
     * @return
     */
    private static <T> boolean containsWithFields(List<T> list, T item, String... fields) {
        return list.stream().anyMatch(item2->matchesFields(item, item2, fields));
    }

    /**
     * 辅助方法,判断两个对象的指定字段是否相等
     *
     * @param item1
     * @param item2
     * @param fields
     * @param <T>
     * @return
     */
    private static <T> boolean matchesFields(T item1, T item2, String... fields) {
        try {
            for (String field : fields) {
                Field declaredField1 = item1.getClass().getDeclaredField(field);
                Field declaredField2 = item2.getClass().getDeclaredField(field);
                declaredField1.setAccessible(true);
                declaredField2.setAccessible(true);
                if (!Objects.equals(declaredField1.get(item1), declaredField2.get(item2))) {
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据指定字段对列表进行去重
     *
     * @param list
     * @param fields
     * @param <T>
     * @return
     */
    public static <T> List<T> distinctByFields(List<T> list, String... fields) {
        return list.stream().filter(distinctByKey(t->getKey(t, fields))).collect(Collectors.toList());
    }

    /**
     * 辅助方法,用于生成去重的键
     *
     * @param keyExtractor
     * @param <T>
     * @return
     */
    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t->seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 辅助方法,生成由指定字段的值组成的键
     *
     * @param item
     * @param fields
     * @param <T>
     * @return
     */
    private static <T> String getKey(T item, String... fields) {
        StringBuilder key = new StringBuilder();
        try {
            for (String field : fields) {
                Field declaredField = item.getClass().getDeclaredField(field);
                declaredField.setAccessible(true);
                key.append(declaredField.get(item)).append("-");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return key.toString();
    }

}
相关推荐
此时不提桶,更待何时13 分钟前
02-02-B-AQS与JUC锁面试与生产事故实战
java
音符犹如代码16 分钟前
Kafka 接入 AI 的三条路线:MCP 提案、会话记忆与实时上下文
java·大数据·ai·kafka
vx_BS8133020 分钟前
【项目编号:project51629】Spring Boot 乡村自来水缴费系统:把抄表、水费生成与在线缴费做成一条数字化闭环
java·spring boot·eclipse·tomcat·maven
qq_2695067528 分钟前
第29课-Servlet基础
java
毅炼40 分钟前
不写多语言 SDK,怎么让 Python、Go、Node 服务接入注册中心?
java·后端·系统架构·gateway
2501_9378609444 分钟前
上篇:网络编程基础与UDP套接字编程
java·网络·计算机网络
泡海椒1 小时前
jquick-pdf 表格行列尺寸控制实战:单元格合并的可行边界
java·开发语言·pdf
Wang's Blog1 小时前
Java 项目实战: 外卖平台-后台系统登录功能开发
java·服务器·redis
海鸥-w1 小时前
spingboot定义一个全局异常处理器
java
曹牧1 小时前
Jackson 反序列化字段名不匹配
java