Сортировка списка файлов в андроид-метании 'Метод сравнения нарушает его общий контракт!' - PullRequest
0 голосов
/ 16 октября 2019

Это происходит в моем приложении Android, и вот трассировка стека:

Caused by java.lang.IllegalArgumentException: Comparison method violates its general contract!
       at java.util.TimSort.mergeHi(TimSort.java:864)
       at java.util.TimSort.mergeAt(TimSort.java:481)
       at java.util.TimSort.mergeForceCollapse(TimSort.java:422)
       at java.util.TimSort.sort(TimSort.java:219)
       at java.util.TimSort.sort(TimSort.java:169)
       at java.util.Arrays.sort(Arrays.java:2023)
       at java.util.Collections.sort(Collections.java:1883)

Вот моя логика сортировки:

private static void sortFiles(List<File> listFiles, int sortDirection) {
    try {
      if (sortDirection == sortLatestFirst) {
        Collections.sort(listFiles, new LatestFirstComparator());
        ...

Вот компаратор:

class LatestFirstComparator implements Comparator<File> {
  @Override
  public int compare(File f1, File f2) {
    return Long.compare(f2.lastModified(), f1.lastModified());
  }
}

Я нашел похожие вопросы и другие решения, но ни один из них не решил мою проблему. Более того, это непоследовательное поведение, а только с некоторыми пользователями приложения.

Ответы [ 2 ]

3 голосов
/ 25 октября 2019

Как говорят другие, проблема заключается в том, что значение последней измененной временной метки может измениться во время операции сортировки. Для надежной сортировки необходимо кэшировать значения на время операции сортировки:

private static void sortFiles(List<File> listFiles, int sortDirection) {
    if(listFiles.isEmpty()) return;
    Map<File,Long> cache = new HashMap<>();
    Comparator<File> byTime
        = Comparator.comparing(f -> cache.computeIfAbsent(f, File::lastModified));
    if(sortDirection == sortLatestFirst) byTime = byTime.reversed();
    listFiles.sort(byTime);
}

Я предполагаю, что вы все равно используете механизм уведомлений об изменениях, чтобы решить, когда следует перезагрузить список файлов,таким образом, операция сортировки не должна иметь с ней дело.

Если вы хотите поддерживать уровень API, который не поддерживает функции Java 8, вы должны использовать более подробный вариант

private static void sortFiles(List<File> listFiles, int sortDirection) {
    if(listFiles.isEmpty()) return;
    final Map<File,Long> cache = new HashMap<File,Long>();
    Comparator<File> byTime = new Comparator<File>() {
        @Override
        public int compare(File f1, File f2) {
            Long t1 = cache.get(f1), t2 = cache.get(f2);
            if(t1 == null) cache.put(f1, t1 = f1.lastModified());
            if(t2 == null) cache.put(f2, t2 = f2.lastModified());
            return t1.compareTo(t2);
        }
    };
    if(sortDirection == sortLatestFirst) byTime = Collections.reverseOrder(byTime);
    Collections.sort(listFiles, byTime);
}
1 голос
/ 23 октября 2019

РЕДАКТИРОВАТЬ: Следуя совету комментарий от пользователя Хольгер Я расширяю ответ, добавляя сюда важные моменты из моих предыдущих комментариев. Они помещаются в блоки цитат.

Как я отмечал в комментариях, наиболее вероятно, что некоторые из сортируемых файлов изменяются во время сортировки. Затем то, что изначально было старым файлом, становится новым файлом во время сортировки :

например, файл A определяется как более новый, чем B, а B - более новый, чем C, но тогда Cизменен и выглядит новее, чем A

Таким образом, отношение заказа «новее» представляется нетранзитивным, что нарушает договор;отсюда ошибка.

Если вам нужны стабильные данные, я полагаю, вы могли бы создавать свои собственные объекты с копиями необходимых атрибутов;тогда порядок будет четко определен, но ... окончательный порядок может оказаться устаревшим, если файлы будут изменены во время сортировки. В любом случае вы не можете избежать этого в многозадачной среде;вы никогда не узнаете, когда какой-либо другой поток или процесс создает, изменяет или удаляет файлы, пока вы не пересканируете каталог

ИМХО, вы можете написать только свою собственную процедуру сортировки, которая будет обрабатывать такую ​​непереходностьбезопасно. Например, он может повторить, когда обнаружит ситуацию. Но будьте осторожны, чтобы не повторять слишком много раз - если два или более файлов постоянно обновляются, процедура повторной сортировки никогда не сможет завершить свою работу!

...