0

I have an Object MyTimes and in that object there are fields name ,start_date and configuration.

I have an array of this object, MyTimes [] mytimes

I am trying to sort the array by the start time but am struggling how to go about it.

The start_time field is a string, so this needs converting to a datetime.

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");


for(int i=0; i<mytimes.length; i++) {
   Date date = formatter.parse(mytimes[i].getStartTime());
}

I'd then put the date into an array list perhaps and then sort by datetime? But then I wouldnt know which start_time corresponds with which mytimes object...

What is the most efficient way of doing this?

arsenal88
  • 1,040
  • 2
  • 15
  • 32
  • 1
    Is there a reason you are using the old java date API instead of the new modern `java.time`? – Zabuzard Mar 08 '19 at 14:39
  • I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `Instant` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). Also make sure that `start_date` is an `Instant` (not a `String`), it makes everything and in particular the sorting much more straightforward. – Ole V.V. Mar 08 '19 at 14:53
  • Adapt the Java 8 solution from [this answer](https://stackoverflow.com/a/43426855/5772882) to your situation. – Ole V.V. Mar 08 '19 at 14:57

3 Answers3

2

You have two main approaches:

  • Make your class implement Comparable
  • Use a custom Comparator

Then, you can choose the field to compare from, and transform it.

IE (implementing comparable):

class Example implements Comparable<Example> { 

    private String stringDate; 

    public int compareTo(Example e) { 
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        Date date1 = formatter.parse(this.stringDate);
        Date date2 = formatter.parse(e.stringDate);
        return date1.getTime() - date2.getTime(); 
    } 
}

And then using Arrays.sort would use your custom comparison.

jbonet
  • 76
  • 7
  • 1
    Is there a reason you are using the old java date API instead of the new modern `java.time`? – Zabuzard Mar 08 '19 at 14:38
  • If `stringDate` really has the given pattern, then there wouldn't be a need to parsing the String to a Date. Natural String sorting would be enough. – Tom Mar 08 '19 at 14:40
  • 1
    Why are you not taking advantage of the fact that Date implements `Comparable` as well? – Joakim Danielson Mar 08 '19 at 14:53
2

Under the right circumstances this is a one-liner:

    Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));

Let’s see it in action:

    MyTimes[] myTimes = {
            new MyTimes("Polly", "2019-03-06T17:00:00Z"),
            new MyTimes("Margaret", "2019-03-08T09:00:00Z"),
            new MyTimes("Jane", "2019-03-01T06:00:00Z")
    };

    Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));

    Arrays.stream(myTimes).forEach(System.out::println);

Output:

Jane     2019-03-01T06:00:00Z
Polly    2019-03-06T17:00:00Z
Margaret 2019-03-08T09:00:00Z

I am assuming that getStartDate returns an Instant or another type the natural order of which agrees with the chronological order you want. For example:

public class MyTimes {

    private String name;
    private Instant startDate;

    // Constructor, getters, toString, etc.
}

If you are receiving your start dates as strings somehow, you may write a convenient constructor that accepts a string for start date. I am already using such a constructor in the above snippet. One possibility is having two constructors:

public MyTimes(String name, Instant startDate) {
    this.name = name;
    this.startDate = startDate;
}

public MyTimes(String name, String startDate) {
    this(name, Instant.parse(startDate));
}

The Instant class is part of java.time, the modern Java date and time API.

I am exploiting the fact that your strings are in the ISO 8601 format for an instant, the format that Instant.parse accepts and parses.

Avoid SimpleDateFormat and Date

I recommend you don’t use SimpleDateFormat and Date. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. There is also an error in your format pattern string for parsing: Z (pronounced “Zulu”) means UTC, and of you don’t parse it as such, you will get incorrect times (on most JVMs). Instant.parse efficiently avoids any problems here.

Don’t store date-tine as a string

It looks like you are are storing start time in a String field in your object? That would be poor modelling. Use a proper date-time type. Strings are for interfaces. Date-time classes like Instant offer much more functionality, for example define sort order.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

Let your class implement Comparable and implement compareTo using modern formatting and date classes. Note that LocalDateTime also implements Comparable so once the string has been parsed you let LocalDateTime do the comparison

public class MyTimes implements Comparable<MyTimes>  {
     private final DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;

     //other code

     public int compareTo(MyTimes o) {
         LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
         LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
         return thisDate.compareTo(otherDate);
     }
}

You can also create a separate class as a comparator if this comparison is special and what you not always want to use

public class MyTimesComparator implements Comparator<MyTimes> {
    @Override
    public int compare(MyTimes arg0, MyTimes arg1) {
        DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
        LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
        LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
        return thisDate.compareTo(otherDate);
    }
}

and then use it like

someList.sort(new MyTimesComparator());

or use an inline function (I am using Instant here)

someList.sort( (m1, m2) -> {
    DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
    Instant instant1 = Instant.from(dtf.parse(m1.getStartTime));
    Instant instant2 = Instant.from(dtf.parse(m2.getStartTime));
    return intant1.compareTo(instant2);
});

I noticed now that you have an array and not a list so you need to convert to a list or use Arrays.sort instead.

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • Thanks for showing the use of `Instant` from java.time, the modern Java date and time API. Using `Instant` is more correct since the string is in this format. From trying to convert to `LocalDateTime` I seem to get `java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {NanoOfSecond=0, MicroOfSecond=0, MilliOfSecond=0, InstantSeconds=1478866394},ISO of type java.time.format.Parsed`. – Ole V.V. Mar 09 '19 at 06:21