1

My logic is eluding me on this one, I'm hoping someone has some code I can learn from.

In java I have a List of custom objects, one of the members of this object is date/time of a certain event.

I need to be able to find the next time in the List from the current/local time.

Ie, The local time is 6pm The list contains:

1pm, 3pm, 4pm, 7pm, 10pm

I need a method to basically pull out 7pm, as the next event time to process.

Any suggestions, directions appreciated, thx

makapaka
  • 169
  • 5
  • 16
  • Have a 'closest time' and assign it as you go through the list – Kevin Hussey Dec 08 '14 at 02:35
  • You could sort the list based on how close an item is to the current time... – MadProgrammer Dec 08 '14 at 02:41
  • 1
    If you use a [`TreeSet`](https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html) with the appropriate comparator, you can call [`ceilling()`](https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html#ceiling-E-) and it'll do what you want. – vandale Dec 08 '14 at 02:52
  • Maybe you should look at this: http://stackoverflow.com/questions/2592501/how-to-compare-dates-in-java – dogwin Dec 08 '14 at 04:16

2 Answers2

0

You can do it by assuming the first dateTime object in the list to be the closest initially and then iterating to find if any other dateTime from the list is more closer to your refernce dateTime.Something like this:

    import java.util.ArrayList;
    import java.util.List;

    import org.joda.time.DateTime;

    public class test {
        public static void main(String[] args) {
            CustomData cd1 = new CustomData("data1", new DateTime(100000));
            CustomData cd2 = new CustomData("data2", new DateTime(200000));
            CustomData cd3 = new CustomData("data3", new DateTime(300000));
            CustomData cd4 = new CustomData("data4", new DateTime(400000));
            List<CustomData> dataList = new ArrayList<CustomData>();

            dataList.add(cd1);
            dataList.add(cd2);
            dataList.add(cd3);
            dataList.add(cd4);

            DateTime closestDate=dataList.get(0).getDateTime();  //initially assume first dateTime to be the closest
            for(CustomData cd:dataList){
                if(cd!=null && cd.getDateTime()!=null && cd.getDateTime().isBefore(closestDate.getMillis()) && cd.getDateTime().isAfter(DateTime.now())){
                    /*if the date time is before the closest date and after the reference DateTime you are comparing(in this case DateTime.now()) update the reference of closestDate */                
                closestDate=cd.getDateTime();
                }
            }

            System.out.println(closestDate);
        }

    }

Also for reference the CustomData class:

import org.joda.time.DateTime;

public class CustomData {
    private String name;
    private DateTime dateTime;

    public CustomData(String name, DateTime dateTime) {
        this.name = name;
        this.dateTime = dateTime;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DateTime getDateTime() {
        return dateTime;
    }

    public void setDateTime(DateTime dateTime) {
        this.dateTime = dateTime;
    }
}
Navish Sharma
  • 233
  • 2
  • 11
0

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)));
    }
}
Christian Hujer
  • 17,035
  • 5
  • 40
  • 47