Assuming that:
- You have a class
Event
which has a time
property,
- the
time
property is of type java.time.LocalTime
or something else which either has a natural order (like java.util.Date
) or for which you can easily provide a Comparator
,
- you have your events in a
java.util.List<Event>
which is unsorted,
- your class
Event
defines a natural order according to time
(implements Comparable<Event>
) or you provide a Comparator<Event>
which compares Event
objects by their time
property,
basically the relevant essentials of class Event
being this,
import java.time.LocalTime;
public class Event implements Comparable<Event> {
LocalTime time;
@Override
public int compareTo(final Event o) {
return time.compareTo(o.time);
}
}
or
import java.time.LocalTime;
public class Event {
LocalTime time;
public static final Comparator<Event> byTime = new Comparator<Event>() {
@Override
public int compare(final Event o1, final Event o2) {
return o1.time.compareTo(o2.time);
}
};
}
then you could use one of the following ways to get the event you're looking for (and there are certainly many more ways):
- You could iterate through the
List
with a for
-each loop (comparative linear search).
- You could
stream()
your list, filter()
all events after that time, sort()
them, and take the first.
- You could put the events in a
TreeMap<LocalTime, Event>
and ask the TreeMap for the ceilingKey()
.
Which of these solutions is best depends on how you actually store your data. If access on List<Event>
is frequently done based on the time
property, you might want to permanently keep a TreeMap<LocalTime, Event>
in your application. In that case, the third solution is best. If it's done rarely, I'd use the Streams solution. The first solution is the naive primitive approach, and although we've been coding that way for decades, I don't like it.
Here are these three solutions:
public static Event getNextEventUsingComparison(final List<Event> unsortedEvents, final LocalTime timestamp) {
Event candidateEvent = null;
for (final Event event : unsortedEvents)
if (event.getTime().isAfter(timestamp) && (candidateEvent == null || event.getTime().isBefore(candidateEvent.getTime())))
candidateEvent = event;
return candidateEvent;
}
public static Event getNextEventUsingStreams(final List<Event> unsortedEvents, final LocalTime timestamp) {
return unsortedEvents.stream().filter(t -> t.getTime().isAfter(timestamp)).sorted().findFirst().orElse(null);
}
public static Event getNextEventUsingTreeMap(final List<Event> unsortedEvents, final LocalTime timestamp) {
final TreeMap<LocalTime, Event> timeEventMap = new TreeMap<>();
for (final Event event : unsortedEvents)
timeEventMap.put(event.getTime(), event);
final LocalTime key = timeEventMap.ceilingKey(timestamp);
return key != null ? timeEventMap.get(key) : null;
}
It should work just as well with other time classes like java.util.Date
instead of java.time.LocalTime
as long as they are implements Comparable
or you can provide a Comparator
.
In case you mess around with this code, you might want to test it. Here's the code relevant to testing which I used for this:
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
public class Event implements Comparable<Event> {
String title;
LocalTime time;
public Event(final String title, final LocalTime time) {
this.title = title;
this.time = time;
}
public Event(final String descriptor) {
this(descriptor.substring(6), LocalTime.parse(descriptor.substring(0, 5)));
}
public String getTitle() { return title; }
public LocalTime getTime() { return time; }
@Override
public int compareTo(final Event o) { return time.compareTo(o.time); }
public static List<Event> createEventList(final String... descriptors) {
final List<Event> events = new ArrayList<>();
for (final String descriptor : descriptors)
events.add(new Event(descriptor));
return events;
}
}
And the Unit Test:
import org.junit.Test;
import java.util.List;
import static java.time.LocalTime.of;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static stackexchange.stackoverflow.q27350515.Event.createEventList;
import static stackexchange.stackoverflow.q27350515.FindNextDate.getNextEvent;
public class FindNextDateTest {
@Test public void givenEmptyList_whenFindingNext_thenReturnsNull() {
assertNull(getNextEvent(emptyList(), of(6, 0)));
}
@Test public void givenOneElementListWithSmallerElement_whenFindingNext_thenReturnsNull() {
final List<Event> events = createEventList("10:15 Breakfast");
assertNull(getNextEvent(events, of(11, 0)));
}
@Test public void givenOneElementListWithLargerElement_whenFindingNext_thenReturnsElement() {
final List<Event> events = createEventList("12:15 Lunch");
assertEquals(events.get(0), getNextEvent(events, of(11, 0)));
}
@Test public void givenBigList_whenFindingNext_thenReturnsElement() {
final List<Event> events = createEventList("10:15 Breakfast", "12:15 Lunch", "08:00 Morning walk", "11:15 Power nap", "14:00 Power nap", "20:00 Dinner");
assertEquals(events.get(3), getNextEvent(events, of(11, 0)));
}
}