2

I am trying to sort an array list in descending order according to the date field in the list. Here is how I extracted out the list of dates in string format:

SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
List<List<String>> list= viewModel.getAllData(reminderlist);

for(int i = 0; i < list.size(); i++){
   System.out.println("DATE " + list.get(i).get(3));
   System.out.println("DATE FORMAT " + formatter.parse(list.get(i).get(3));
}

The sample data for the println are:

"09/05/2019", "10/16/2017", "06/24/2020", "10/16/2017", "10/17/2015"

Then, I found some solution online but I not sure how to actually apply them onto my case since mine has no object, therefore I cannot do something like Collections.sort(myList, new Comparator<MyObject> {}

QWERTY
  • 2,303
  • 9
  • 44
  • 85
  • 1
    Lists are objects too. – shmosel Jul 17 '18 at 01:28
  • 1
    never compare dates as string in american format – Jacek Cz Jul 17 '18 at 01:29
  • Use a `Comparator` – MadProgrammer Jul 17 '18 at 01:33
  • For parsing you should use a format pattern string that matches your date strings (in this case without time of day). You should sort date objects, prefereably `LocalDate`, not date strings. And under all circumstances you should avoid the long outdated and notoriously `SimpleDateFormat` class. I recommend [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter` instead. – Ole V.V. Jul 17 '18 at 04:23
  • It’s true, @shmosel, but the `List` interface doesn’t extend `Comparable`, so the first answer to the linked question will not work as it stands. Of course lists can be sorted, for example using a comparator. Under all circumstances having the data as a list of lists looks to me like poor data modelling at first sight, though we don’t have enough background to judge. – Ole V.V. Jul 17 '18 at 04:28

3 Answers3

4

I believe you can just negate the comparison to switch the order. Something like

      return -1*(o2.get(3).compareTo(o1.get(3)));
Nicholas Hirras
  • 2,592
  • 2
  • 21
  • 28
1

I managed to sort it in descending order by using the code below:

     SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    Collections.sort(templateDirs, (o1, o2) -> {
        if (o1.get(3) == null || o2.get(3) == null)
            return 0;
        try {
            boolean b =  formatter.parse(o1.get(3)).before(formatter.parse(o2.get(3)));
            return b ? 1:-1 ;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    });
Jswq
  • 758
  • 1
  • 7
  • 23
QWERTY
  • 2,303
  • 9
  • 44
  • 85
  • 1
    You need to convert the string the date object before comparation. eg: "11/05/2014" > "10/16/2017" in string form – Jswq Jul 17 '18 at 01:59
  • @Jaycee Thanks so much! But I am getting unhandled parse exception from the edit above. Then if I added the catch statement, I am getting "Bad return type: boolean cannot be converted to int" at the return !formatter that line – QWERTY Jul 17 '18 at 02:54
  • Oh~ you can convert boolean to integer – Jswq Jul 17 '18 at 03:23
  • 1
    you can do this change "return formatter.parse(o2.get(3)).compareTo((formatter.parse(o1.get(3)));" – Keaz Jul 17 '18 at 04:00
  • Thanks for providing your own solution. It will not work. You need to decide where nulls should go in the sorted order. You are like trying to say “just put them somewhere”, which may cause the sorting to throw an exception. Also be consistent about the value for rows with the same date (I suggest you return 0 from your comparator). – Ole V.V. Jul 17 '18 at 05:07
0

Best solution: Get your data modeling right. Use a proper class for the objects in your list instead of the inner lists. In that class use LocalDate for your dates. Avoid the Date class, it is long outdated and in spite of its name doesn’t represent a date, but a point in time, which is not what you need. Whether your class should implement Comparable or should be sorted using a comparator is a design decision where I cannot advice based on the information I have. You can find many good examples of both out there.

There may be very special circumstances that dictate that a list of lists is right here, again I don’t have the information to tell, but for the majority of purposes it is not so.

Second best solution if each date occurs at most once: Put all your inner lists into a TreeMap with LocalDate objects as keys and Comparator.reverseOrder() as comparator. Again avoid Date and certainly avoid SimpleDateFormat. The latter class is notoriously troublesome. Use a DateTimeFormatter for parsing the date strings into LocalDate objects. Then take all the values from the map out into a list, and it will be sorted by the dates in descending order. Using this way each date will only be parsed once instead of each time it is being compared. Since you have October 16, 2017, twice in your data, modify this solution so that the map values are lists of lists rather than just lists (of strings).

Poor solution: Sort your list of lists using a comparator that takes out the element at index 3 from each inner list, parses it and compares it. Use the following comparator:

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/uuuu");
        Comparator<List<String>> descendingDateComparator = Comparator
                .comparing((List<String> l) -> LocalDate.parse(l.get(3), dateFormatter))
                .reversed();

Using Comparator.comparing and friends for specifying a comparator is not just terser, it is first and foremost less error-prone.

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