Java中使用流Stream查找列表中的最大和最小日期

在本文中,我们将探讨如何使用Streams在这些对象列表中查找最大和最小日期。

示例设置
Java 的原始Date API 仍然被广泛使用,因此我们将展示一个使用它的示例。但是,自 Java 8 以来,引入了LocalDate ,并且大多数Date方法已被弃用。因此,我们还将展示一个使用LocalDate的示例。

首先,让我们创建一个包含单独Date属性的基本Event对象:

public class Event {
    Date date;
    // constructor, getter and setter
}

要将天数添加到日期,我们将使用Apache Commons 的 DateUtils方法addDays()。为此,我们需要将最新版本的库添加到我们的pom.xml中:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

现在我们可以定义三个事件的列表:第一个事件发生在今天,第二个事件发生在明天,第三个事件发生在一周内:

Date TODAY = new Date();
Event TODAYS_EVENT = new Event(TODAY);
Date TOMORROW = DateUtils.addDays(TODAY, 1);
Event TOMORROWS_EVENT = new Event(TOMORROW);
Date NEXT_WEEK = DateUtils.addDays(TODAY, 7);
Event NEXT_WEEK_EVENT = new Event(NEXT_WEEK);
List<Event> events = List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT);

我们的目标是编写一个方法,能够确定NEXT_WEEK_EVENT是此Event列表中的最大日期。我们还将使用LocalDate而不是Date执行相同操作。我们的LocalEvent将如下所示:

public class LocalEvent {
    LocalDate date;
    // constructor, getter and setter
}

构建事件列表更加简单,因为LocalDate已经有内置的plusDays()方法:

LocalDate TODAY_LOCAL = LocalDate.now();
LocalEvent TODAY_LOCAL_EVENT = new LocalEvent(TODAY_LOCAL);
LocalDate TOMORROW_LOCAL = TODAY_LOCAL.plusDays(1);
LocalEvent TOMORROW_LOCAL_EVENT = new LocalEvent(TOMORROW_LOCAL);
LocalDate NEXT_WEEK_LOCAL = TODAY_LOCAL.plusWeeks(1);
LocalEvent NEXT_WEEK_LOCAL_EVENT = new LocalEvent(NEXT_WEEK_LOCAL);
List<LocalEvent> localEvents = List.of(TODAY_LOCAL_EVENT, TOMORROW_LOCAL_EVENT, NEXT_WEEK_LOCAL_EVENT);

获取最大日期
首先,我们将使用Stream API来流式传输我们的事件列表。然后,我们需要将Date getter 应用于Stream的每个元素。这样,我们将获得一个包含事件日期的Stream 。我们现在可以对其使用max()函数。这将返回Stream中与提供的Comparator相关的最大日期。

Date类实现了Comparable<Date>。因此,compareTo()方法定义了自然的日期顺序。简而言之,可以在max()中等效地调用以下两个方法:

  1. Date的compareTo()可以通过方法引用来引用
  2. Comparator的naturalOrder()可以直接使用

最后,请注意,如果给定的事件列表为 null 或为空,我们可以直接返回 null。这将确保我们在流式传输列表时不会遇到问题。

该方法最终如下所示:

Date findMaxDateOf(List<Event> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(Event::getDate)
      .max(Date::compareTo)
      .get();
}

或者,使用naturalOrder(),它将读取:

Date findMaxDateOf(List<Event> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(Event::getDate)
      .max(Comparator.naturalOrder())
      .get();
}

总而言之,我们现在可以快速测试我们的方法是否为我们的列表返回正确的结果:

assertEquals(NEXT_WEEK, findMaxDateOf(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT);

对于LocalDate,道理完全一样。LocalDate确实实现了ChronoLocalDate 接口,该接口扩展了Comparable<ChronoLocalDate> 。 因此, LocalDate的自然顺序由ChronoLocalDate的compareTo()方法定义。

因此,该方法可以写成:

LocalDate findMaxDateOf(List<LocalEvent> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(LocalEvent::getDate)
      .max(LocalDate::compareTo)
      .get();
}

或者,以完全等效的方式:

LocalDate findMaxDateOf(List<LocalEvent> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(LocalEvent::getDate)
      .max(Comparator.naturalOrder())
      .get();
}

我们可以编写以下测试来确认它有效:

assertEquals(NEXT_WEEK_LOCAL, findMaxDateOf(List.of(TODAY_LOCAL_EVENT, TOMORROW_LOCAL_EVENT, NEXT_WEEK_LOCAL_EVENT)));

获取最小日期
类似地,我们可以在日期列表中找到最小日期:

Date findMinDateOf(List<Event> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(Event::getDate)
      .min(Date::compareTo)
      .get();
}

我们可以看到,唯一的变化是我们使用了min()函数而不是max()。让我们验证一下它是否给出了三个日期中最早的日期:

@Test
void givenEventList_whenFindMinDateOf_thenReturnMinDate() {
    assertEquals(TODAY, DateHelper.findMinDateOf(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT)));
}

如果我们使用LocalDate,我们将使用此方法:

LocalDate findMaxDateOfLocalEvents(List<LocalEvent> events) {
    if (events == null || events.isEmpty()) {
        return null;
    }
    return events.stream()
      .map(LocalEvent::getDate)
      .max(LocalDate::compareTo)
      .get();
}

再次,我们所做的唯一更改是用对min()方法的调用替换对max()的调用。最后,我们也可以测试它:

@Test
void givenEventList_whenFindMinDateOfWithComparator_thenReturnMaxDate() {
    assertEquals(TODAY, DateHelper.findMinDateOfWithComparator(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT)));
}

结论
在本教程中,我们了解了如何获取对象列表中的最大或最小日期。我们使用了Date和LocalDate对象。