【问题标题】:Java - Stream Filter to get Last Day of several months in a list of dates (not calendar last day)Java - 流过滤器以获取日期列表中几个月的最后一天(不是日历最后一天)
【发布时间】:2022-01-02 03:57:50
【问题描述】:

查询: 过滤下面的数据以在列表中查找每个月的最后日期。请注意,在这种情况下, 数据中的最后一个月的日期可能是或 可能与日历月的最后一个日期不匹配。 预期的输出显示在第二个列表中。

研究:

  • 我相信TemporalAdjusters.lastDayOfMonth() 在这种情况下不会有帮助,因为列表中的最后一个日期可能与日历月的最后一个日期匹配,也可能不匹配。
  • 我在 Stack Overflow 上检查了几个问题,并在 Google 上搜索过, 但我找不到与我的需求相似的东西。

我希望问题很清楚,并为我指明了如何使用流来完成此操作的方向, 因为我不想使用for 循环。

样本数据:

Date Model Start End
27-11-1995 ABC 241 621
27-11-1995 XYZ 3456 7878
28-11-1995 ABC 242 624
28-11-1995 XYZ 3457 7879
29-11-1995 ABC 243 627
29-11-1995 XYZ 3458 7880
30-11-1995 ABC 244 630
30-11-1995 XYZ 3459 7881
01-12-1995 ABC 245 633
01-12-1995 XYZ 3460 7882
04-12-1995 ABC 246 636
04-12-1995 XYZ 3461 7883
27-12-1995 ABC 247 639
27-12-1995 XYZ 3462 7884
28-12-1995 ABC 248 642
28-12-1995 XYZ 3463 7885
29-12-1995 ABC 249 645
29-12-1995 XYZ 3464 7886
01-01-1996 ABC 250 648
01-01-1996 XYZ 3465 7887
02-01-1996 ABC 251 651
02-01-1996 XYZ 3466 7888
29-01-1996 ABC 252 654
29-01-1996 XYZ 3467 7889
30-01-1996 ABC 253 657
30-01-1996 XYZ 3468 7890
31-01-1996 ABC 254 660
31-01-1996 XYZ 3469 7891

Screenshot

需要输出:

Date Model Start End
30-11-1995 ABC 244 630
30-11-1995 XYZ 3459 7881
29-12-1995 ABC 249 645
29-12-1995 XYZ 3464 7886
31-01-1996 ABC 254 660
31-01-1996 XYZ 3469 7891

Screenshot

【问题讨论】:

    标签: java java-stream yearmonth


    【解决方案1】:

    嗯,groupingBymaxBy 的组合可能就可以了。

    我假设表中的每条记录都是Event 类型:

    record Event(LocalDate date, String model, int start, int end) { }
    

    要获取表中该月的最后几天,我们可以使用groupingBy。为了对其进行分组,我们可以首先创建一个分组类型。下面,我创建了一个EventGrouping 记录1,使用static 方法将Event 转换为EventGrouping。您想要的输出表明您希望按年-月-模型组合进行分组,因此我们只选择了这两个属性:

    public record EventGrouping(YearMonth yearMonth, String model) {
            
        public static EventGrouping fromEvent(Event event) {
            return new EventGrouping(YearMonth.from(event.date()), event.model());
        }
    }
    

    然后,我们可以得到我们想要的结果:

    events.stream()
        .collect(Collectors.groupingBy(
            EventGrouping::fromEvent,
            Collectors.maxBy(Comparator.comparing(Event::date))
        ));
    

    这里发生的情况是,所有流元素都按我们的EventGrouping 分组,然后选择每个事件组的“最大值”。最大值当然是该月的最近日期。

    请注意,maxBy 在组为空的情况下返回 Optional。另请注意,生成的 Map 是无序的。

    我们可以分别使用collectingAndThen地图工厂来解决这两个问题:

    Map<EventGrouping, Event> map = events.stream()
        .collect(groupingBy(
            EventGrouping::fromEvent,
            () -> new TreeMap<>(Comparator.comparing(EventGrouping::yearMonth)
                .thenComparing(EventGrouping::model)),
            collectingAndThen(maxBy(Comparator.comparing(Event::date)), Optional::get)
        ));
    

    注意:groupingBycollectingAndThenmaxBy 都是从 java.util.stream.Collectors 静态导入的。

    • 我们添加了SupplierTreeMapTreeMapMap 实现,具有给定比较器的可预测顺序。这允许我们遍历按年-月-模型排序的结果条目。
    • collectingAndThen 允许我们将函数应用于给定 Collector 的结果。如前所述,maxBy 返回一个Optional,因为如果源流中没有元素,maxBy 不适用。但是,在我们的案例中,这永远不会发生。所以我们可以安全地将Optional 映射到它所包含的值。

    1 除了编写自定义类型之外,您还可以使用包含两个任意值的现有类,例如 Map.EntryPair 甚至 List&lt;Object&gt;

    【讨论】:

    • 它给出一个错误:lambda 表达式中的返回类型错误:TreeMap 无法转换为 TreeMap 我在这里遗漏了什么吗?
    • @iCoder 这可能是编译器无法推断出正确的类型。在 JDK 17 下它可以工作。你用的是哪个编译器?
    • 如果您在此处使用() -&gt; new TreeMap&lt;EventGrouping, Event&gt;… 对编译器有所帮助,它可能也会起作用。
    • 我正在使用 IntelliJ Ultimate 和 JDK - 16.0.2 如果我帮助编译器按照建议进行推断,则会出现错误 - i.imgur.com/fAbjdNr.png
    • 那么,data 是什么类型?
    【解决方案2】:

    我建议创建一个Map&lt;YearMonth, List&lt;LocalDate&gt; 并解析您的所有日期并填写您的地图。之后,您对所有列表进行排序,每个列表中的最后一个(或第一个取决于排序顺序)值将是您想要的值

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多