如何利用多Predicate过滤器分组Person列表,以过滤器名为键生成目标Map
按多Predicate分组Person列表的通用实现
需求说明
给定Person类、一组Predicate<Person>过滤器(以名称为键存入filterMap,支持任意数量),需要将输入的List<Person>分组为Map<String, List<Person>>,每个键对应符合对应Predicate的Person列表,要求仅执行一次遍历操作,支持通用处理N个过滤器的场景。
基础定义与示例数据
Person类与过滤器定义
class Person { private String status; private String id; // 构造方法 public Person(String status) { this.status = status; } public Person(String status, String id) { this.status = status; this.id = id; } // Getter方法 public String getStatus() { return status; } public String getId() { return id; } } // 定义过滤器Predicate Predicate<Person> completedP = j -> j.getStatus().equalsIgnoreCase("completed"); Predicate<Person> inProgressP = j -> j.getStatus().equalsIgnoreCase("inprogress"); Predicate<Person> nullStatusP = j -> j.getStatus() == null && j.getId() == null; // 过滤器名称与Predicate的映射 Map<String, Predicate<Person>> filterMap = Map.of( "completedP", completedP, "inProgressP", inProgressP, "nullStatusP", nullStatusP );
示例输入
Person p1 = new Person("completed"); Person p2 = new Person("completed"); Person p3 = new Person("inprogress"); Person p4 = new Person("inprogress"); Person p5 = new Person(null, null); Person p6 = new Person(null, null); List<Person> people = List.of(p1, p2, p3, p4, p5, p6);
期望输出
- 键
completedP对应列表:[p1, p2] - 键
inProgressP对应列表:[p3, p4] - 键
nullStatusP对应列表:[p5, p6]
解决方案
高效单次遍历实现
这种方式仅遍历一次people列表,对每个Person检查所有过滤器,将符合条件的对象加入对应分组,是性能最优的实现方式:
// 初始化结果Map,提前为每个过滤器创建空列表 Map<String, List<Person>> result = filterMap.keySet().stream() .collect(Collectors.toMap( key -> key, key -> new ArrayList<>() )); // 单次遍历所有Person,匹配过滤器并分组 for (Person person : people) { filterMap.forEach((filterName, predicate) -> { if (predicate.test(person)) { result.get(filterName).add(person); } }); }
简洁Stream实现(注意:多遍历)
如果追求代码简洁,也可以用Stream API实现,但该方式会对每个过滤器遍历一次people列表,当过滤器数量N较大时性能会下降:
Map<String, List<Person>> result = filterMap.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, entry -> people.stream() .filter(entry.getValue()) .collect(Collectors.toList()) ));
互斥过滤器优化
如果你的过滤器是互斥的(一个Person最多匹配一个过滤器),可以在找到第一个匹配项后停止检查,进一步优化性能:
for (Person person : people) { for (Map.Entry<String, Predicate<Person>> entry : filterMap.entrySet()) { if (entry.getValue().test(person)) { result.get(entry.getKey()).add(person); break; // 匹配后终止当前Person的过滤器检查 } } }
说明
- 所有实现都保证了通用性,无论过滤器数量是3个还是N个,都无需修改核心逻辑。
- 初始化结果Map时提前创建空列表,避免后续操作出现空指针,同时保证即使没有匹配的Person,结果中也会保留对应过滤器的键和空列表。
内容的提问来源于stack exchange,提问作者curiousengineer
相关产品推荐
相关产品推荐

