我的过程对象列表如下
Procedure1 01/01/2020 Procedure2 03/01/2020 Procedure3 03/01/2020 Procedure1 04/01/2020 Procedure5 05/01/2020, 02/01/2020 Procedure2 06/01/2020我的Procedure类就像
and my Procedure class is like
Class Procedure { List<Date> procedureDate; String procedureName; }我要根据以下条件对对象进行分类和分组.
I want to sort and group the objects based on the below conditions.
最终结果必须是
Procedure2 06/01/2020 Procedure2 03/01/2020 Procedure5 05/01/2020, 02/01/2020 Procedure1 04/01/2020 Procedure1 01/01/2020 Procedure3 03/01/2020我能够使用Comparator和旧的Java代码来实现这一目标.使用java8流,收集器和分组依据是否可以实现相同的目的?
I was able to achieve this using Comparator and old java code. Is it possible to achieve the same using java8 streams, collectors and grouping by?
推荐答案这是一个非常有趣的问题.解决方案不像看起来那么简单.您必须将解决方案分为多个步骤:
This is a very interesting question. The solution is not as easy as it looks to be. You have to divide the solution into multiple steps:
这里是演示: www.jdoodle/iembed/v0 /Te .
第1步
List<Procedure> procedures = ... Map<String, Date> map = procedures.stream().collect( Collectors.collectingAndThen( Collectors.groupingBy( Procedure::getProcedureName, Collectors.maxBy(Comparatorparing(s -> s.getProcedureDate().get(0)))), s -> s.entrySet().stream() .filter(e -> e.getValue().isPresent()) .collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().get().getProcedureDate().get(0)))));..解释:有一种简单的方法来获取具有procedureName分组的最大首个日期的Procedure.
.. explained: There is a simple way to get a Procedure with maximum first date grouped by procedureName.
Map<String, Optional<Procedure>> mapOfOptionalProcedures = procedures.stream() .collect(Collectors.groupingBy( Procedure::getProcedureName, Collectors.maxBy(Comparatorparing(o -> o.getProcedureDate().get(0)))));但是,返回的结构有点笨拙(Map<String, Optional<Procedure>>),要使其有用并直接返回Date,就需要附加的下游收集器Collectors::collectingAndThen,该收集器使用Function作为结果映射器:
However, the returned structure is a bit clumsy (Map<String, Optional<Procedure>>), to make it useful and return Date directly, there is a need of additional downstream collector Collectors::collectingAndThen which uses a Function as a result mapper:
Map<String, Date> map = procedures.stream().collect( Collectors.collectingAndThen( /* grouping part */, s -> s.entrySet().stream() .filter(e -> e.getValue().isPresent()) .collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().get().getProcedureDate().get(0)))));...实际上是第一个片段.
... which is effectively the first snippet.
第2步,第3步和第4步
基本上,按每个组的最大日期排序.然后按名称排序,最后按实际的第一个日期排序.
Basically, sort by the maximum date for each group. Then sort by the name and finally by the actual first date.
Collections.sort( procedures, (l, r) -> { int dates = map.get(r.getProcedureName())pareTo(map.get(l.getProcedureName())); if (dates == 0) { int names = l.getProcedureName()pareTo(r.getProcedureName()); if (names == 0) { return r.getProcedureDate().get(0)pareTo(l.getProcedureDate().get(0)); } else return names; } else return dates; } );排序结果
根据您的问题使用不推荐使用的java.util.Date,排序后的procedures将对项目进行排序,例如您的预期代码段(我已经覆盖了Procedure::toString方法)
Using the deprecated java.util.Date according to your question, the sorted procedures will have sorted items like your expected snippet (I have overrided the Procedure::toString method)
@Override public String toString() { return procedureName + " " + procedureDate; }Procedure2 [Mon Jan 06 00:00:00 CET 2020] Procedure2 [Fri Jan 03 00:00:00 CET 2020] Procedure5 [Sun Jan 05 00:00:00 CET 2020, Thu Jan 02 00:00:00 CET 2020] Procedure1 [Sat Jan 04 00:00:00 CET 2020] Procedure1 [Wed Jan 01 00:00:00 CET 2020] Procedure3 [Fri Jan 03 00:00:00 CET 2020]