0

I need to sort an array of files by the date which is part of the name of the file e.g.: "20200611_2130.dat". I tried doing it with:

Arrays.sort(files,new FileNameComparator());
public class FileNameComparator implements Comparator<File> {
    private static SimpleDateFormat formatter =
            new SimpleDateFormat("YYYYddMM_HHmm");

    @Override
    public int compare(File a, File b) {
        try {
            return asTime(a.getName()) > asTime(b.getName()) ? 1 : -1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    private static long asTime(String filename) throws ParseException {
        return formatter.parse(
                filename.substring(0, filename.lastIndexOf("."))).getTime();

    }
}

I know there is a lot to fix here, but right now I would like to understand why I get:

"Comparison method violates its general contract!"

Bertolla
  • 3
  • 2
  • Does this answer your question? [What is the compare contract?](https://stackoverflow.com/questions/15765158/what-is-the-compare-contract) – PM 77-1 Dec 29 '20 at 17:01
  • Looks like the date and time make up the entire name (apart from the extension) and looks like the format is greatest to smallest. A String comparison should give the correct result. – DarkMatter Dec 29 '20 at 18:12
  • @DarkMatter The format is not greatest to smallest, since day comes before months, but thanks for the input – Bertolla Dec 30 '20 at 07:46
  • @PM77-1 Thanks for the pointer, now I need to figure out what part of the contract I'm not fulfilling. I guess it's the equal comparison, like some have already pointed out – Bertolla Dec 30 '20 at 07:50
  • Yup, my bad. Should have read more carefully. – DarkMatter Dec 30 '20 at 08:27

3 Answers3

1

Your comparator not works when a==b. It has to return 0. Your current implementation returns -1. You should consider the case where a==b and return 0 if true, then handle the case where filenames are different.

codiallo
  • 173
  • 4
0

@Bertolla you need to add a equals case. if equal return 0 else -1. then do a greater or smaller comparison case.

Kush Singh
  • 157
  • 3
  • 11
0

Code review:

  • Change your pattern to "yyyyddMM_HHmm" if 20200611_2130.dat is Fri Nov 06 21:30:00.
  • You should return something in the catch block, for example Long.MIN_VALUE.
  • Add a second comparator if the filename is not a date, for example compare as strings.

Your code might look something like this:

private static SimpleDateFormat formatter =
        new SimpleDateFormat("yyyyddMM_HHmm");

private static long asTime(String filename) {
    try {
        Date date = formatter.parse(
                filename.substring(0, filename.lastIndexOf(".")));
        return date.getTime();
    } catch (ParseException e) {
        return Long.MIN_VALUE;
    }
}
public static void main(String[] args) {
    String[] fileNames = {
            "20200611_2130.dat",
            "20201511_2130.dat",
            "20200605_2130.dat",
            "20100611_2130.dat",
            "picture.jpg"};

    Arrays.sort(fileNames, Comparator
            .<String>comparingLong(name -> asTime(name))
            .thenComparing(Comparator.naturalOrder()));

    Arrays.stream(fileNames).forEach(System.out::println);
}

Output:

picture.jpg
20100611_2130.dat
20200605_2130.dat
20200611_2130.dat
20201511_2130.dat