理论上,使用两个单独列表的方法应该快比使用两步的方法Comparator,因为基于一个字段的比较显然比基于两个字段的比较快.通过使用两个列表,您可以加速具有O(n log n) 时间复杂度(排序)的算法部分,但代价是增加了具有时间复杂度O(n) 的额外初始阶段(分成两部分)。由于n log n > n,对于非常非常大的n 值,两个列表方法应该更快。
然而,在实践中,我们谈论的时间差异如此之小,以至于在两个列表方法胜出之前,您必须拥有非常长的列表,因此在您开始遇到诸如此类的问题之前,使用列表来证明差异是非常困难的作为OutOfMemoryError。
但是,如果您使用数组而不是列表,并且使用巧妙的技巧来完成它而不是使用单独的数据结构,则可以击败两步 Comparator 方法,如下面的代码所示。在任何人抱怨之前:是的,我知道这不是一个合适的基准!
尽管sort2 比sort1 快,但我可能不会在生产代码中使用它。最好使用熟悉的习语和明显有效的代码,而不是使用更难理解和维护的代码,即使它稍微快一些。
public class Main {
static Random rand = new Random();
static Compound rand() {
return new Compound(rand.nextBoolean(), rand.nextLong());
}
static Compound[] randArray() {
int length = 100_000;
Compound[] temp = new Compound[length];
for (int i = 0; i < length; i++)
temp[i] = rand();
return temp;
}
static class Compound {
boolean bool;
long time;
Compound(boolean bool, long time) {
this.bool = bool;
this.time = time;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Compound compound = (Compound) o;
return bool == compound.bool && time == compound.time;
}
@Override
public int hashCode() {
int result = (bool ? 1 : 0);
result = 31 * result + (int) (time ^ (time >>> 32));
return result;
}
}
static final Comparator<Compound> COMPARATOR = new Comparator<Compound>() {
@Override
public int compare(Compound o1, Compound o2) {
int result = (o1.bool ? 0 : 1) - (o2.bool ? 0 : 1);
return result != 0 ? result : Long.compare(o1.time, o2.time);
}
};
static final Comparator<Compound> LONG_ONLY_COMPARATOR = new Comparator<Compound>() {
@Override
public int compare(Compound o1, Compound o2) {
return Long.compare(o1.time, o2.time);
}
};
static void sort1(Compound[] array) {
Arrays.sort(array, COMPARATOR);
}
static void sort2(Compound[] array) {
int secondIndex = array.length;
if (secondIndex == 0)
return;
int firstIndex = 0;
for (Compound c = array[0];;) {
if (c.bool) {
array[firstIndex++] = c;
if (firstIndex == secondIndex)
break;
c = array[firstIndex];
} else {
Compound c2 = array[--secondIndex];
array[secondIndex] = c;
if (firstIndex == secondIndex)
break;
c = c2;
}
}
Arrays.sort(array, 0, firstIndex, LONG_ONLY_COMPARATOR);
Arrays.sort(array, secondIndex, array.length, LONG_ONLY_COMPARATOR);
}
public static void main(String... args) {
// Warm up the JVM and check the algorithm actually works.
for (int i = 0; i < 20; i++) {
Compound[] arr1 = randArray();
Compound[] arr2 = arr1.clone();
sort1(arr1);
sort2(arr2);
if (!Arrays.equals(arr1, arr2))
throw new IllegalStateException();
System.out.println(i);
}
// Begin the test proper.
long normal = 0;
long split = 0;
for (int i = 0; i < 100; i++) {
Compound[] array1 = randArray();
Compound[] array2 = array1.clone();
long time = System.nanoTime();
sort1(array1);
normal += System.nanoTime() - time;
time = System.nanoTime();
sort2(array2);
split += System.nanoTime() - time;
System.out.println(i);
System.out.println("COMPARATOR: " + normal);
System.out.println("LONG_ONLY_COMPARATOR: " + split);
}
}
}