常用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();
    }

}
相关推荐
潜意识起点几秒前
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
java·spring boot·后端
mxbb.1 分钟前
单点Redis所面临的问题及解决方法
java·数据库·redis·缓存
云和数据.ChenGuang26 分钟前
《XML》教案 第1章 学习XML基础
xml·java·学习
王·小白攻城狮·不是那么帅的哥·天文34 分钟前
Java操作Xml
xml·java
发飙的蜗牛'44 分钟前
23种设计模式
android·java·设计模式
music0ant1 小时前
Idean 处理一个项目引用另外一个项目jar 但jar版本低的问题
java·pycharm·jar
陈大爷(有低保)1 小时前
logback日志控制台打印与写入文件
java
繁川1 小时前
深入理解Spring AOP
java·后端·spring
Am心若依旧4091 小时前
[c++进阶(三)]单例模式及特殊类的设计
java·c++·单例模式
ZHOUPUYU4 小时前
最新 neo4j 5.26版本下载安装配置步骤【附安装包】
java·后端·jdk·nosql·数据库开发·neo4j·图形数据库