如何用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
相关产品推荐
相关产品推荐

