对对象列表进行排序和分组 [英] Sorting and Grouping on a list of objects

查看:147
本文介绍了对对象列表进行排序和分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的过程对象列表如下

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.

  1. 所有过程均应根据过程名称进行分组.
  2. 程序必须按程序日期的降序排列. [日期列表中的第一个元素,即procedureDate.get[0]]
  3. 归类在一起的相同过程应按日期降序排列.

最终结果必须是

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:

  1. 基于List<Date>中的第一个日期获取每个分组的procedureName的最大值.
  2. 根据第一步中创建的Map<String, Date的最大Date值比较Procedure实例.
  3. 如果它们相等,则用名称区分它们(例如两次Procedure 2).
  4. 如果它们仍然相等,请根据它们的实际第一个日期对Procedure实例进行排序.
  1. Get the max value for each grouped procedureName based on the first dates in the List<Date>.
  2. Compare the Procedure instances based on max Date value from the Map<String, Date created in the step one.
  3. If they are equal distinguish them by the name (ex. two times Procedure 2).
  4. If they are still equal, sort the Procedure instances based on their actual first date.

这里是演示: https://www.jdoodle.com/iembed/v0 /Te .

第1步

List<Procedure> procedures = ...

Map<String, Date> map = procedures.stream().collect(
    Collectors.collectingAndThen(
        Collectors.groupingBy(
            Procedure::getProcedureName,
            Collectors.maxBy(Comparator.comparing(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(Comparator.comparing(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()).compareTo(map.get(l.getProcedureName()));
        if (dates == 0) {
             int names =  l.getProcedureName().compareTo(r.getProcedureName());
             if (names == 0) {
                 return r.getProcedureDate().get(0).compareTo(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]

这篇关于对对象列表进行排序和分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆