1. Overview
In this article, we’ll explore how to find the maximal and minimal date in a list of those objects using Streams.
2. Example Setup
Java’s original Date API is still widely used, so we’ll showcase an example using it. However, since Java 8, LocalDate was introduced, and most Date methods were deprecated. Thus, we’ll also show an example that uses LocalDate.
Firstly, let’s create a base Event object that contains a lone Date property:
public class Event {
Date date;
// constructor, getter and setter
}
To add days to a Date, we’ll use Apache Commons’ DateUtils method addDays(). For that purpose, we need to add the latest version of the library to our pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
We can now define a list of three Event: the first one taking place today, the second one tomorrow, and the third one in a week:
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);
Our goal is now to write a method that will be able to determine that NEXT_WEEK_EVENT is the maximal date in this Event list. We’ll also do the same with a LocalDate instead of a Date. Our LocalEvent will look like this:
public class LocalEvent {
LocalDate date;
// constructor, getter and setter
}
Building the Event list is a bit more straightforward since LocalDate already has a built-in plusDays() method:
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);
3. Get the Max Date
To start, we’ll use the Stream API to stream our Event list. Then, we’ll need to apply the Date getter to each element of the Stream. Thus, we’ll obtain a Stream containing the dates of the events. We can now use the max() function for it. This will return the maximal Date in the Stream regarding the provided Comparator.
The Date class implements Comparable<Date>. As a result, the compareTo() method defines the natural date order. In a nutshell, it is possible to equivalently call the two following methods inside max():
- Date‘s compareTo() can be referred to via a method reference
- Comparator‘s naturalOrder() can be used directly
Lastly, let’s note that if the given Event list is null or empty, we can directly return null. This will ensure we don’t run into issues while streaming the list.
The method finally looks like this:
Date findMaxDateOf(List<Event> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(Event::getDate)
.max(Date::compareTo)
.get();
}
Alternatively, with naturalOrder(), it would read:
Date findMaxDateOf(List<Event> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(Event::getDate)
.max(Comparator.naturalOrder())
.get();
}
To conclude, we can now quickly test that our method returns the correct result for our list:
assertEquals(NEXT_WEEK, findMaxDateOf(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT);
With LocalDate, the reasoning is exactly the same. LocalDate indeed implements the ChronoLocalDate interface, which extends Comparable<ChronoLocalDate>. Thus, the natural order for LocalDate is defined by ChronoLocalDate‘s compareTo() method.
As a result, the method can be written:
LocalDate findMaxDateOf(List<LocalEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(LocalEvent::getDate)
.max(LocalDate::compareTo)
.get();
}
Or, in a completely equivalent way:
LocalDate findMaxDateOf(List<LocalEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(LocalEvent::getDate)
.max(Comparator.naturalOrder())
.get();
}
And we can write the following test to confirm it works:
assertEquals(NEXT_WEEK_LOCAL, findMaxDateOf(List.of(TODAY_LOCAL_EVENT, TOMORROW_LOCAL_EVENT, NEXT_WEEK_LOCAL_EVENT)));
4. Get the Min Date
Similarly, we can find the minimal date in a Date list:
Date findMinDateOf(List<Event> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(Event::getDate)
.min(Date::compareTo)
.get();
}
As we can see, the only change is that we used the min() function instead of max(). Let’s verify it gives us the earliest date of the three:
@Test
void givenEventList_whenFindMinDateOf_thenReturnMinDate() {
assertEquals(TODAY, DateHelper.findMinDateOf(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT)));
}
If we are working with LocalDate, we’ll use this method:
LocalDate findMaxDateOfLocalEvents(List<LocalEvent> events) {
if (events == null || events.isEmpty()) {
return null;
}
return events.stream()
.map(LocalEvent::getDate)
.max(LocalDate::compareTo)
.get();
}
Once again, the only change we made is replacing the call to max() with a call to the min() method. Finally, we can also test it:
@Test
void givenEventList_whenFindMinDateOfWithComparator_thenReturnMaxDate() {
assertEquals(TODAY, DateHelper.findMinDateOfWithComparator(List.of(TODAYS_EVENT, TOMORROWS_EVENT, NEXT_WEEK_EVENT)));
}
5. Conclusion
In this tutorial, we saw how to get the maximum or minimum date in a list of objects. We’ve used both Date and LocalDate objects.
As always, the code can be found over on GitHub.