You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Stream合并两个集合并基于Predicate筛选唯一值(不依赖equals)

使用Java Stream合并两个集合并基于自定义Predicate去重

刚好遇到过类似的场景,我来给你捋捋怎么实现。核心思路是先把两个集合合并成一个Stream,然后通过自定义逻辑过滤掉重复元素——这里的关键是怎么用Predicate来判定“重复”,而不是依赖对象的equals方法。

先补全你提到的Employee类和枚举定义,方便后续示例:

enum Type {
    CEO, OfficeManager, SalesPerson
}

class Employee {
    private int id;
    private String firstName;
    private String lastName;
    private Type type;

    // 构造器
    public Employee(int id, String firstName, String lastName, Type type) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.type = type;
    }

    // Getter方法
    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }

    // 重写toString方便测试输出
    @Override
    public String toString() {
        return "Employee{id=" + id + ", name='" + firstName + " " + lastName + "'}";
    }
}

接下来分两种常见场景给出实现方案:

1. 基于单字段的简单判定(比如ID相同算重复)

如果你的重复判定逻辑是单字段(比如员工ID相同就算同一个人),用Collectors.toMap是最高效的方式——把判定字段作为Map的Key,对象作为Value,遇到重复Key时选择保留哪一个,最后把Map的Values转成List即可。

代码示例:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class EmployeeMergeDemo {
    public static void main(String[] args) {
        List<Employee> list1 = Arrays.asList(
                new Employee(1, "Adam", "Smith", Type.CEO),
                new Employee(2, "Bob", "Jones", Type.OfficeManager),
                new Employee(3, "Carl", "Lewis", Type.SalesPerson)
        );

        List<Employee> list2 = Arrays.asList(
                new Employee(2, "Bob", "Smith", Type.OfficeManager), // ID和list1的Bob重复
                new Employee(4, "Dave", "Brown", Type.SalesPerson),
                new Employee(1, "Adam", "Johnson", Type.CEO) // ID和list1的Adam重复
        );

        // 合并并去重:按ID判定重复,保留第一个出现的元素
        List<Employee> mergedUniqueList = Stream.concat(list1.stream(), list2.stream())
                .collect(Collectors.toMap(
                        Employee::getId, // 用ID作为去重Key
                        emp -> emp, // Value为员工对象
                        (existingEmp, newEmp) -> existingEmp // 重复时保留先出现的元素
                ))
                .values()
                .stream()
                .collect(Collectors.toList());

        mergedUniqueList.forEach(System.out::println);
        // 输出结果:
        // Employee{id=1, name='Adam Smith'}
        // Employee{id=2, name='Bob Jones'}
        // Employee{id=3, name='Carl Lewis'}
        // Employee{id=4, name='Dave Brown'}
    }
}

2. 基于复杂Predicate的自定义判定(比如姓名组合相同算重复)

如果你的重复逻辑更复杂(比如firstName + lastName相同就算重复,不管ID),可以用一个辅助集合记录已处理元素,结合filter和自定义Predicate实现。

代码示例:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class EmployeeMergeComplexDemo {
    public static void main(String[] args) {
        List<Employee> list1 = Arrays.asList(
                new Employee(1, "Adam", "Smith", Type.CEO),
                new Employee(2, "Bob", "Jones", Type.OfficeManager),
                new Employee(3, "Carl", "Lewis", Type.SalesPerson)
        );

        List<Employee> list2 = Arrays.asList(
                new Employee(5, "Bob", "Jones", Type.SalesPerson), // 姓名和list1的Bob重复
                new Employee(4, "Dave", "Brown", Type.SalesPerson),
                new Employee(6, "Adam", "Smith", Type.Manager) // 姓名和list1的Adam重复
        );

        // 自定义Predicate:姓名组合相同就算重复
        List<Employee> seenEmployees = new ArrayList<>();
        List<Employee> mergedUniqueList = Stream.concat(list1.stream(), list2.stream())
                .filter(emp -> {
                    // 检查已处理列表中是否有符合重复条件的元素
                    boolean isDuplicate = seenEmployees.stream()
                            .anyMatch(e -> e.getFirstName().equals(emp.getFirstName())
                                    && e.getLastName().equals(emp.getLastName()));
                    if (!isDuplicate) {
                        seenEmployees.add(emp);
                    }
                    return !isDuplicate;
                })
                .collect(Collectors.toList());

        mergedUniqueList.forEach(System.out::println);
        // 输出结果:
        // Employee{id=1, name='Adam Smith'}
        // Employee{id=2, name='Bob Jones'}
        // Employee{id=3, name='Carl Lewis'}
        // Employee{id=4, name='Dave Brown'}
    }
}

额外优化:封装可复用的去重工具方法

如果需要多次使用自定义Predicate去重,可以把过滤逻辑抽成工具方法:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class StreamUtils {
    public static <T> Predicate<T> distinctByPredicate(Predicate<T> duplicateChecker) {
        List<T> seen = new ArrayList<>();
        return t -> seen.stream().noneMatch(duplicateChecker) && seen.add(t);
    }
}

使用时直接调用:

.filter(StreamUtils.distinctByPredicate(e -> 
    e.getFirstName().equals(emp.getFirstName()) && e.getLastName().equals(emp.getLastName())
))

内容的提问来源于stack exchange,提问作者j5423951

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:47:01