android 中的文件排序列表抛出 'Comparison method violates its general contract!'
Sorting list of files in android throwing 'Comparison method violates its general contract!'
它发生在我的 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());
}
}
我找到了相关问题和其他解决方案,但其中 none 解决了我的问题。
此外,这不是一贯的行为,而是仅在某些应用程序用户中发生。
编辑:根据 by User Holger 的建议,我通过在此处添加我之前评论中的要点来扩展答案。它们被放在引号块中。
正如我在评论中指出的,很可能一些正在排序的文件在排序过程中被修改了。然后,最初是旧文件的文件在 排序期间变成了新文件::
e.g. file A is detected as newer than B, and B newer than C, but then C is modified and appears newer than A
因此排序关系'is newer'出现非传递性,这违背了契约;因此错误。
If you need stable data, I suppose you could make your own objects with copies of necessary attributes; then the ordering will be well-defined, but... the final order may appear obsolete if files are modified during sorting. Anyway you can't avoid that in a multitasking environment; you'll never know when some other thread or process creates, modifies or deletes files until you re-scan the directory
恕我直言,您只能编写自己的排序例程,它可以安全地处理此类非传递性。例如,它可以在检测到情况时重申。但注意不要迭代太多次 - 如果两个或多个文件不断更新,重新排序例程将永远无法完成其工作!
正如其他人所说,问题在于最后修改时间戳的值在排序操作期间可能会发生变化。为了可靠地排序,您必须在排序操作期间缓存值:
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);
}
它发生在我的 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());
}
}
我找到了相关问题和其他解决方案,但其中 none 解决了我的问题。 此外,这不是一贯的行为,而是仅在某些应用程序用户中发生。
编辑:根据
正如我在评论中指出的,很可能一些正在排序的文件在排序过程中被修改了。然后,最初是旧文件的文件在 排序期间变成了新文件::
e.g. file A is detected as newer than B, and B newer than C, but then C is modified and appears newer than A
因此排序关系'is newer'出现非传递性,这违背了契约;因此错误。
If you need stable data, I suppose you could make your own objects with copies of necessary attributes; then the ordering will be well-defined, but... the final order may appear obsolete if files are modified during sorting. Anyway you can't avoid that in a multitasking environment; you'll never know when some other thread or process creates, modifies or deletes files until you re-scan the directory
恕我直言,您只能编写自己的排序例程,它可以安全地处理此类非传递性。例如,它可以在检测到情况时重申。但注意不要迭代太多次 - 如果两个或多个文件不断更新,重新排序例程将永远无法完成其工作!
正如其他人所说,问题在于最后修改时间戳的值在排序操作期间可能会发生变化。为了可靠地排序,您必须在排序操作期间缓存值:
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);
}