ArrayLists 似乎使用 TimSort 排序,其中底层列表在排序期间并不总是一致的。在调用比较器时,列表条目可能会消失或出现两次。
在我们的比较器中,我们正在比较键,我们正在使用一个函数来获取一个值来比较这个键。由于此函数在其他上下文中使用,我们测试了键是否实际存在于列表中(排序中不需要的东西):
if (keys.contains(itemId)) {
...
由于键是我们正在排序的列表,因此由于 TimSort 的内部机制,比较器可能会在列表中找不到键。
问题:这是否在 Javadoc 中的某处(找不到)提到您不应该访问 Comparator 中的底层列表?这是应该对副本进行排序的 TimSort 的糟糕实现吗?还是首先访问比较器中的基础列表是一个愚蠢的想法?
下面的程序由TJ Crowder提供,演示了底层列表的内容在调用 Comparator 期间可能不一致。(这个程序演示了有问题的现象,但它并不代表受问题影响的实际应用程序。)
import java.util.*;
public class Example {
private static String[] chars = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
};
private List<String> list;
private String[] entries;
private Example() {
this.entries = new String[1000];
for (int n = 0; n < 1000; ++n) {
this.entries[n] = chars[n % chars.length] + n;
}
// Ensure it's an ArrayList, specifically
this.list = new ArrayList<String>(Arrays.asList(this.entries));
}
public static void main(String[] args) {
(new Example()).run();
}
class ListComparator implements Comparator<String> {
public int compare(String a, String b) {
for (String s : entries) {
int i1 = Example.this.list.indexOf(s);
if (i1 == -1) {
System.out.println(s + ": Missing");
} else {
int i2 = Example.this.list.lastIndexOf(s);
if (i2 != i1) {
System.out.println(s + ": Duplicated, at " + i1 + " and " + i2);
}
}
}
return a.compareTo(b);
}
}
private void run() {
this.list.sort(new ListComparator());
}
}
以下是运行的前几行输出:
b1:缺失 a52:重复,在 2 和 32 b27:失踪 a52:重复,在 2 和 32 c2:失踪 a52:重复,在 2 和 32 c2:失踪 c28:失踪 a52:重复,在 2 和 32 b53:重复,在 5 和 33 c28:失踪 d29:缺失 a52:重复,在 2 和 32 b53:重复,在 5 和 33 d3:缺失 d29:缺失 a52:重复,在 2 和 32 b53:重复,在 5 和 33 d3:缺失 d29:缺失 e30:失踪