如何用Java Streams获取两个不同对象列表中的非匹配Bar对象
用Java Streams筛选未匹配Foo的Bar对象
先明确你的核心需求:从barList中找出所有不存在对应匹配Foo的Bar对象,匹配规则是:
Bar.barName忽略大小写后与Foo.name一致Bar.codeBar等于Foo.age转换后的Integer值
先聊聊你写的代码里存在的几个问题:
- 方法调用错误:
f.getName应该是f.getName(),age也需要调用f.getAge()(你漏了方法的括号) - 逻辑判断偏差:你用
anyMatch(f -> !条件1 && 条件2)的逻辑,会把只要存在某个Foo不满足"barName匹配"但满足codeBar的Bar都留下来,完全不符合你要的"未匹配任何Foo"的逻辑 - 性能隐患:如果
foolist数据量较大,每个Bar都遍历整个Foo列表会导致O(n*m)的时间复杂度,效率很低
正确的基础实现(直接用Streams)
要实现"未匹配任何Foo"的判断,应该用noneMatch方法——它会验证当前Bar是否没有任何一个Foo符合匹配规则,代码如下:
List<Bar> unmatchedBars = barList.stream() .filter(bar -> foolist.stream() .noneMatch(foo -> bar.getBarName().equalsIgnoreCase(foo.getName()) && bar.getCodeBar() == Integer.parseInt(foo.getAge()) ) ) .collect(Collectors.toList());
⚠️ 注意:如果Foo.age可能存在非整数的情况,需要添加异常处理(比如用try-catch包裹Integer.parseInt,或者提前清理Foo列表的age字段)
优化性能的实现(提前构建匹配键集合)
如果你的foolist数据量较大,上面的方法效率会很低。我们可以提前把所有Foo的匹配规则提取成一个键集合,这样判断Bar是否匹配的时间复杂度就降到O(1):
Java 16+ 版本(用Record定义匹配键)
// 定义匹配键的record,自动实现equals和hashCode record FooMatchKey(String normalizedName, int code) {} // 提前构建Foo的匹配键集合 Set<FooMatchKey> fooMatchKeys = foolist.stream() .map(foo -> new FooMatchKey(foo.getName().toLowerCase(), Integer.parseInt(foo.getAge()))) .collect(Collectors.toSet()); // 筛选未匹配的Bar List<Bar> unmatchedBars = barList.stream() .filter(bar -> !fooMatchKeys.contains( new FooMatchKey(bar.getBarName().toLowerCase(), bar.getCodeBar()) )) .collect(Collectors.toList());
Java 16之前版本(用SimpleEntry替代)
// 提前构建Foo的匹配键集合 Set<Map.Entry<String, Integer>> fooMatchKeys = foolist.stream() .map(foo -> new AbstractMap.SimpleEntry<>( foo.getName().toLowerCase(), Integer.parseInt(foo.getAge()) )) .collect(Collectors.toSet()); // 筛选未匹配的Bar List<Bar> unmatchedBars = barList.stream() .filter(bar -> !fooMatchKeys.contains( new AbstractMap.SimpleEntry<>( bar.getBarName().toLowerCase(), bar.getCodeBar() ) )) .collect(Collectors.toList());
这种处理方式把整体时间复杂度从O(n*m)降到了O(n+m),数据量越大性能提升越明显。
内容的提问来源于stack exchange,提问作者Juan
相关产品推荐
相关产品推荐

