Android中的文件排序列表抛出'Comparison方法违反了其一般约定!'

问题描述 投票:0回答:2

这正在我的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());
  }
}

我已经找到相关的问题和其他解决方案,但是没有一个解决我的问题。而且,这不是一致的行为,而是仅在某些应用程序用户中发生。

file sorting comparator file-comparison timsort
2个回答
1
投票

正如其他人所说,问题在于最后一个修改的时间戳的值可能在排序操作期间发生变化。为了可靠地排序,您必须在排序操作期间缓存值:

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

我假设您仍然使用通知机制进行更改,以决定何时重新加载文件列表,因此排序操作不需要处理它。

如果要支持不支持Java 8功能的API级别,则必须使用更详细的变体

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
投票

编辑:以下来自the comment的用户Holger的建议,我通过在此处添加我以前的评论中的要点来扩展答案。它们放在引号中。

正如我在评论中指出的,很有可能正在排序的某些文件在排序过程中被修改。然后,最初是旧文件的文件变为新文件期间排序:

例如检测到文件A比B更新,并且B比C更新,但是随后C被修改并显得比A更新]

因此,订购关系“较新”显得不可传递,这破坏了合同;因此错误。

如果您需要稳定的数据,我想您可以使用必要属性的副本制作自己的对象;那么排序将是明确定义的,但是...如果在排序过程中修改了文件,则最终顺序可能会过时。无论如何,在多任务环境中您无法避免这种情况。您将永远不知道其他线程或进程何时创建,修改或删除文件,直到您重新扫描目录

恕我直言,您只能编写自己的排序例程,该例程将安全地处理此类非传递性。例如,它可以在检测到情况时重申。但是请注意不要重复太多-如果两个或多个文件不断更新,重新排序例程将永远无法完成工作!

© www.soinside.com 2019 - 2024. All rights reserved.