Getting the next time in a Java List - java

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

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;
}
}

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)));
}
}

Related

Method to return the same interface implementation as passed in

I've seen similar questions, but I don't really feel like I have an understanding of how to solve this. I want to make a method that takes a list of any class that implements an interface and returns an object of the same class.
I was hoping something like this would work.
public interface IHasDate {
public Date getDate();
}
public class A implements IHasDate {
private Date date;
public Date getDate(){ return date; }
}
public class B implements IHasDate {
private Date date;
public Date getDate(){ return date; }
}
public class Util {
public IHasDate getUpcoming(List<IHasDate> dates) {
// logic omitted to find the object with the next upcoming date
return dates.get(next);
}
}
public class X {
private List<A> aList;
public A getUpcoming() {
return Util.getUpcoming(aList);
}
}
What I'm really trying to do is just avoid writing getUpcoming(List<?> aList) for each class that has a list of IHasDate's.
Is there a better strategy for what I'm trying to do?
Right now I've been able to get around it by having a function that takes a list of IHasDate's and returns the index of the upcoming object in the list, but I have to create a new list of IHasDate's from aList first and it all just feels kind of clunky.
What I was hoping for was something along these lines.
public T getUpcoming(List<T implements IHasDate> dates) {
//...
}
You are sooo close. 🙂
public <T extends IHasDate> T getUpcoming(List<T> dates) {
//...
}

How to do different types of object state validation

I need to provide records to the caller from one or two different data sources and either within a specified date range or year range.
My dilemma is should I use overloaded methods or a Request object with state validation logic.
So either:
public List<Record> getRecords (Date fromDate, Date toDate, boolean dataSourceARequired, boolean dataSourceBRequired)
public List<Record> getRecords (int fromYear, int toYear, boolean dataSourceARequired, boolean dataSourceBRequired)
or something like this:
public List<Record> getRecords(Request request)
where Request would look something like:
public class Request{
private final Date fromDate;
private final Date toDate;
private final int fromYear;
private final int toYear;
private final boolean dataSourceARequired;
private final boolean dataSourceBRequired;
public Request(Date fromDate, Date toDate, boolean dataSourceARequired, boolean dataSourceBRequired){
if (fromDate == null) {
throw new IllegalArgumentException("fromDate can't be null");
}
if (toDate == null) {
throw new IllegalArgumentException("toDate can't be null");
}
if (!dataSourceARequired && !dataSourceBRequired){
throw new IllegalStateException ("No data source requested");
}
if (fromDate.after(toDate)){
throw new IllegalStateException ("startDate can't be after endDate");
}
this.fromDate = fromDate;
this.toDate = toDate;
this.dataSourceARequired = dataSourceARequired;
this.dataSourceBRequired = dataSourceBRequired;
this.fromYear = -1;
this.toYear = -1;
}
public Request(int fromYear, int toYear, boolean dataSourceARequired, boolean dataSourceBRequired){
if (fromYear > toYear) {
throw new IllegalArgumentException("fromYear can't be greater than toYear");
}
if (!dataSourceARequired && !dataSourceBRequired){
throw new IllegalStateException ("No data source requested");
}
this.dataSourceARequired = dataSourceARequired;
this.dataSourceBRequired = dataSourceBRequired;
this.fromYear = fromYear;
this.toYear = toYear;
this.fromDate = null;
this.toDate = null;
}
}
Or is there another way?
You should not use the second case as it breaks the rule that every class should have a single well-defined responsibility. Here, your class is responsible for both detailed date ranges and year date ranges. If you added more criteria this class would grow into something monstrous.
So you can use the first method and many people do.
If you want to create classes to encapsulate request data you should create a base abstract class or interface and then have a different type of request subclass for each type of criteria that you could use. For example:
public interface Request {
execute();
}
public class YearRangeRequest implements Request {
int fromYear;
int toYear;
public execute();
... etc
Another solution: Use a DateRange class with two constructors:
public class DateRange {
final Date start;
final Date end;
public DateRange(Date start, Date end) {
this.start = start;
this.end = end;
}
public DateRange(int startYear, int endYear) {
this(yearBegining(startYear), yearBegining(endYear));
}
...
}

Trying to store Bukkit ItemStack field to a file but ItemStack is not Serrializable

ItemStack is not serializable and I'm trying to save an object called Objective with an ItemStack field to a file but since ItemStack is not serializable this does not work. I tried extending ItemStack and implementing Serializable and changing the field to my new serializable sub class but this also did not work. Here are the relevant parts of my original code:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import org.bukkit.inventory.ItemStack;
public class Objective implements Serializable {
private static final long serialVersionUID = -2018456670240873538L;
private static ArrayList<Requirement> requirements = new ArrayList<>();
private String name;
private Requirement requirement;
private ItemStack reward;
private int tillComplete;
private boolean complete;
public Objective(String name, int requirementIndex, int tillComplete, ItemStack reward) {
if(requirements.isEmpty()) {
requirements.add(Requirement.kill_Skeletons);
requirements.add(Requirement.kill_Spiders);
requirements.add(Requirement.kill_Zombies);
}
this.name = name;
this.requirement = requirements.get(requirementIndex);
this.tillComplete = tillComplete;
this.reward = reward;
complete = false;
}
public String getName() {
return name;
}
public Object getRequirement() {
return requirement;
}
public static ArrayList<Requirement> getRequirements() {
return requirements;
}
public static void setRequirements(ArrayList<Requirement> requirements) {
Objective.requirements = requirements;
}
public int getTillComplete() {
return tillComplete;
}
public void setTillComplete(int tillComplete) {
this.tillComplete = tillComplete;
}
public void setName(String name) {
this.name = name;
}
public void setRequirement(Requirement requirement) {
this.requirement = requirement;
}
public void setReward(ItemStackSerializable reward) {
this.reward = reward;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
public ItemStack getReward() {
return reward;
}
public boolean isComplete() {
return complete;
}
}
Elsewhere in my code this line of code:
ItemStack reward = new ItemStack(Material.DIAMOND_SWORD);
Objective objective = new Objective(args[1] ,Integer.parseInt(args[2]), Integer.parseInt(args[3]), reward);
is giving me this error:
java.io.NotSerializableException: org.bukkit.inventory.ItemStack
How can I serialize this object? I need to store it but Java wont let me. Thanks for your help. If you need anymore code snippets or other information please let me know.
This is most likely an XY problem. ItemStack is not serializable, as it represents an actual stack of items at runtime. This would not make sense to store in a file. It might be worth looking at the ConfigurationSerializable interface, which is implemented by ItemStack, but I don't think you need that here.
In your example, the ItemStack that you are trying to serialize doesn't have any metadata. It is a single diamond sword. If all your rewards are just a single item, all you need to save is the Material and you can create a new ItemStack from that Material every time you want to give a player a reward. Since Material is an enum, it is serializable by default.
You could also serialize an int for stack size if some of your rewards require a stack of multiple items. If you need any metadata, serialize the data needed to construct that at runtime. For example, to give an item lore, you would store a List<String> (with a serializable implementation of List).

JavaFX - Bind LocalTime to TableColumn via Property

I am using JavaFX to wrap existing classes with properties, so that I may bind them to a GUI directly without having to manually hook up events. Here is a sample of my code:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.ObjectProperty;
import controller.NewOrder;
import controller.Types.Action;
public class ObservableOrder extends NewOrder
{
private StringProperty m_account;
private ObjectProperty<Action> m_action;
public ObservableOrder()
{
// CONSTRUCT PROPERTIES
m_account = new SimpleStringProperty(this, "m_account");
m_action = new SimpleObjectProperty<Action>(this, "m_action", Action.BUY);
}
// GETTERS
#Override public String account() { return m_account.get(); }
#Override public Action action() { return m_action.get(); }
// SETTERS
#Override public void account(String v) { accountProperty().set(v); }
#Override public void action(Action v) { actionProperty().set(v); }
// PROPERTY GETTERS
public StringProperty accountProperty() { return m_account; }
public ObjectProperty<Action> actionProperty() { return m_action; }
}
This works for integers, strings and enums. What I would like to do next is wrap a LocalTime object with properties and bind it to a table column, but I can't figure it out. Should I use ObservableValue instead? Any help is appreciated. Thx

The Method is undefined for the type Library(class)

How do i use the method Loanable to get setDueOn(calendar.getTime) and setLoanedTo(name) to function?
"Your library loan method should use the Loanable item that is passed in to set the due on and the loaned to. You want to call those set methods on that passed in instance of Loanable. "
I am getting the errors
The method setDueOn is undefined for the type library
The method setLoanedTo is undefined for the type library
Sorry for all the code.
package src.edu.htc.java1.library;
import java.util.ArrayList;
import java.util.Calendar;
public class Library {
/* The collection of Media owned by the library */
private ArrayList<Media> collection = new ArrayList<Media>();
public void addToCollection(Media item) {
collection.add(item);
}
public void loan(Loanable item,String name) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, item.getDaysToLoan()); // Now this is asking the helper how many days to loan for
setDueOn(calendar.getTime());
setLoanedTo(name);
return(setdueOn(Loanable));
}
}
1
package src.edu.htc.java1.library;
import java.util.ArrayList;
/**
*
*/
/**
* This class is used to test our library and media classes.
*
*/
public class MediaTester {
/**
* This is the main test method
* #param args - values passed in by the JVM when running
* the application
*/
public static void main(String[] args) {
Book myBook = new Book();
myBook.setLibraryId(123456L);
myBook.setLocation("Eden Prairie");
myBook.setTitle("My Book Title");
ArrayList<String> authorList = new ArrayList<String>();
authorList.add("Joe Author");
authorList.add("Jane Author");
myBook.setAuthors(authorList);
myBook.setCopyright("1984");
myBook.setFormat("paperback");
myBook.setNumberOfPages(385);
myBook.setPublishers("Some Publisher");
System.out.println(myBook);
Movies myMovie = new Movies();
myMovie.setTitle("Fargo");
myMovie.setReleaseDate(2123);
myMovie.setDirector("Matt Johnson");
myMovie.setActors("Matt");
myMovie.setMPAA_rating("R");
ArrayList<String> actors = new ArrayList<String>();
actors.add("Tom Hanks");
System.out.println(myMovie);
Games myGames = new Games();
myGames.setTitle("Starcraft");
myGames.setConsoleType("wii");
myGames.setEsbnRatings("E");
myGames.setReleaseDate("2012");
myGames.setPublishers("Blizzard");
System.out.println(myGames);
Newspaper myPaper = new Newspaper();
myPaper.setLibraryId(11122233L);
myPaper.setLocation("St. Paul");
myPaper.setTitle("Pioneer Press");
ArrayList<Media> myMediaList = new ArrayList<Media>();
myMediaList.add(myBook);
myMediaList.add(myMovie);
myMediaList.add(myGames);
myMediaList.add(myPaper);
Library myLibrary = new Library();
for (Media item : myMediaList) {
myLibrary.addToCollection(item);
if (item instanceof Loanable) {
myLibrary.loan((Loanable) item, "Mary");
System.out.println(String.format("Item loaned out: %s",item));
} else {
System.out.println(
String.format("Sorry you can't loan out this item: %s", item));
}
}
}
}
2
package src.edu.htc.java1.library;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
*
*/
/**
* This class represents an item in our library's collection.
*
*/
public abstract class Media {
public static final int STANDARD_LOAN_DAYS = 14;
private long libraryId;
private String title;
private String location;
private String loanedTo;
private Date dueOn;
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{");
builder.append(this.getClass().getSimpleName());
builder.append(": ");
builder.append("[id=").append(libraryId);
builder.append("], [title=").append(title);
builder.append("], [loanedTo=").append(loanedTo);
if (getDueOn() != null) {
builder.append("], [dueOn=");
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
builder.append(formatter.format(getDueOn()));
}
builder.append("]");
builder.append(toStringHelper());
builder.append("}");
return builder.toString();
}
public long getLibraryId() {
return libraryId;
}
public void setLibraryId(long libraryId) {
this.libraryId = libraryId;
}
protected String toStringHelper() {
return " ";
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getLoanedTo() {
return loanedTo;
}
public Date getDueOn() {
return dueOn;
}
public void setDueOn(Date dueOn) {
this.dueOn = dueOn;
}
public void setLoanedTo(String loanedTo) {
this.loanedTo = loanedTo;
}
}
3
Your Media class might need to implement the Loanable interface like pointed out in the comments if the inheriting classes don't, and then this method:
public void loan(Loanable item,String name) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, item.getDaysToLoan()); // Now this is asking the helper how many days to loan for
setDueOn(calendar.getTime());
setLoanedTo(name);
return(setdueOn(Loanable));
}
has a few errors that need to be fixed:
If you declare a method void it means it can't (and shouldn't) return anything, so don't use any return statement in it.
The method calls setDueOn and setLoanedTo needs to be called on a specific object, in this case item. At the moment you're trying to call methods by that name local to the Library class, where they don't exist.
If you look at the method setDueOn(Date dueOn) you'll note that it expects a date, not a class or interface name...
After corrections the method might look like this:
public void loan(Loanable item,String name) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, item.getDaysToLoan());
item.setDueOn(calendar.getTime());
item.setLoanedTo(name);
}
There might be other issues, but the code is really a bit too long to browse through...

Categories

Resources