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

}
相关推荐
CRMEB系统商城1 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源
keyipatience2 小时前
线程栈与TLS和线程互斥
java·linux·服务器·开发语言·ubuntu
weixin_408099672 小时前
2026 图片去水印 API 接口完全指南:一键去除图片水印(附 Python/Java/PHP/C# 示例)
java·python·php·图片处理·api调用·图片去水印·石榴智能
钱六两3 小时前
#3、SpringAI 接入deepSeek大模型(喂饭版)
java·spring boot·ai编程
null_174 小时前
IntelliJ IDEA 极致流畅配置方案:Ultra 9 285K + 64GB 内存实测
java·ide·intellij-idea
Herbert_hwt4 小时前
建立Java程序开发
java·开发语言
好好沉淀4 小时前
@ExcelIgnoreUnannotated 和 @AutoMapper 详解
java
愚公移码5 小时前
蓝凌EKP18产品:流程虚拟机(PVM)
java·开发语言·前端
驰骋工作流5 小时前
流程引擎BPM设计之:流程消息
java·工作流引擎·bpm·jflow·ccflow
dogstarhuang5 小时前
从 0 到 1 搭建可收费的 API 开放平台(实战)
java·架构·api