Java8 Stream流之多字段去重

最近有一个需求,需要在做多表关联查询的同时使用其中某一个表中的两个字段进行去重,乍一听是没有问题的,第一反应是使用DISTINCT函数进行去重,当使用时却发现,DISTINCT函数定义在第一个开始的字段(即select后开始的第一个字段),如果用在其他位置会报【缺失表达式】,在我的个业务逻辑中是不满足的(因为我需要查询其他字段的同时根据某两个字段进行去重),当即问了度娘,给的提示是使用GROUP BY分组函数进行分组,但是我是使用的oracle数据库,如果使用GROUP BY分组函数的话就必须使用COUNT函数对所有非COUNT列进行GROUP BY,但显然也不满足业务需求,需要执行的sql中没有需要COUNT的列。后来又问了chatGPT,给出的方案是可以通过窗口函数ROW_NUMBER()和PARTITION BY来进行去重,但得出的结果和使用上面两种类似,无法在给定查询多个字段的同时对其中的两个字段进行去重且实现起来也相对前两种复杂(其实是自己之前用窗口函数比较少),于是当即问了业务这个需求实现方案是谁提供的,从业务那里得知另外一位开发小伙伴已经实现了这个功能(心想,不会是写了一个巨复杂的sql吧),结果和心里想的类似,是通过代码进行过滤的,虽然会有一丢丢的性能问题,但是能实现就可以忽略不记(这里是想说,当使用代码过滤的前提是将数据查询出来,那么查询出来的数据肯定要比过滤后的数据多,如果数据量不大的情况下其实性能问题可以忽略,但是如果数据量较大的情况其实查询上还是会有优化的空间)

我看了一下开发小伙伴的代码,发现是使用了流的方式进行过滤去重

list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getUserName() + ";" + o.getPassWord()))), ArrayList::new));

使用流的集合collect方法,以及TreeSet和默认排序方法进行函数式编程,由于在一个流中,可以连很长。通过 Collectors.toCollection 方法创建了一个 TreeSet 实例,并将其传递给了 collectingAndThen 方法。由于没有提供比较器(Comparator),TreeSet 将使用元素的自然顺序进行排序。将列表中的元素收集到一个按照自然顺序排序的 TreeSet 中,并将 TreeSet 作为最终的结果返回。

也可以用另外一种方式,去重的字段可以是一个也可以是多个

list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(User::getUserName).thenComparing(User::getPassWord))
), ArrayList<SurvivalMonitor>::new));

通过这样获取的集合就是使用相关字段进行去重后的集合了,之前在用流的时候很少做这样很长的链式编程,同时collect方法也是单独使用的居多,我查了一下整个服务中只有一处使用了这种方式,学习到了一种没有碰到的新方法,后面或多或少会用到,所以在这里先记录一下

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

*

712 次浏览