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

}
相关推荐
小昭在路上……1 分钟前
编译与链接的本质:段(Section)的生成与定位
java·linux·开发语言
启山智软16 分钟前
【智能商城系统技术架构优势】
java·spring·开源·商城开发
迷藏49418 分钟前
# 发散创新:基于Solidity的NFT智能合约设计与部署实战在区块链技术飞速发展
java·区块链·智能合约
tq108619 分钟前
从对象互操作性角度分析 `from` 与 `to` 方法的选择
java
IT 行者34 分钟前
实战LangChain4j集成MCP Server:让Java AI应用具备工具调用能力
java·开发语言·人工智能
ok_hahaha37 分钟前
java从头开始-黑马点评-商户查询缓存
java·spring·缓存
LINgZone21 小时前
Java Mock 测试框架 Mockito
java·windows·microsoft
SWAGGY..1 小时前
【C++初阶】:(5)内存管理
java·c++·算法
XiYang-DING1 小时前
【Java SE】包装类(Wrapper Class)
java·开发语言
麦兜顶当当1 小时前
subprocess与子进程交互
java·开发语言·jvm