Java 根据两个标准对列表排序的最佳算法是什么?
我有一个列表,我需要根据两个标准进行排序。 第一个标准是Java 根据两个标准对列表排序的最佳算法是什么?,java,algorithm,list,sorting,Java,Algorithm,List,Sorting,我有一个列表,我需要根据两个标准进行排序。 第一个标准是布尔值,比如说是大的。第二个是长的,表示时间戳 我需要以这种方式对列表中的元素进行排序:在isBig=true之前,然后是isBig=false。在这些组中,单个元素应根据其时间戳按降序排列 基本上,我希望结果是这样的: isBig - 2015/10/29 isBig - 2015/10/28 isBig - 2015/10/27 !isBig - 2015/10/30 !isBig - 2015/10/27 !isBig - 2015/
布尔值
,比如说是大的
。第二个是长的
,表示时间戳
我需要以这种方式对列表中的元素进行排序:在isBig=true
之前,然后是isBig=false
。在这些组中,单个元素应根据其时间戳按降序排列
基本上,我希望结果是这样的:
isBig - 2015/10/29
isBig - 2015/10/28
isBig - 2015/10/27
!isBig - 2015/10/30
!isBig - 2015/10/27
!isBig - 2015/10/26
假设对象是这样的:
public class Item {
Boolean isBig;
Long timestamp;
// ...
}
而列表就是列表列表
我发现有一种方法是循环三次:第一种方法是组成两组:isBig
和!isBig
。第二个和第三个用于对其中的元素进行排序。最后,我合并了这两个列表
是否有更有效的算法根据两个标准对列表进行排序?您可以使用检查两个标准的自定义比较方法直接对列表进行排序 使用
Collections.sort
方法并将方法compare
覆盖的自定义比较器传递给:
int compare(Item o1, Item o2) {
if (o1.isBig && !o2.isBig)
return -1;
if (!o1.isBig && o2.isBig)
return 1;
if (o1.timestamp < o2.timestamp)
return -1;
if (o1.timestamp > o2.timestamp)
return 1;
return 0;
}
它没有条件分支,这意味着它可能比上面的朴素版本更快
这可能是为提高性能所能做的一切。如果我们对您的数据了解更多(例如,大对象总是比小对象多,或者所有时间戳都是唯一的,或者所有时间戳都来自某个狭窄的范围等等),我们可能会提出更好的解决方案。然而,当我们假设您的数据是任意的并且没有特定的模式时,最好的解决方案是使用标准的排序实用程序,如我上面所示
将列表拆分为两个子列表并分别排序肯定会更慢。实际上,排序算法很可能将数据分成两组,然后递归地分成四组,以此类推。但是,分区不会遵循
isBig
标准。如果您想了解更多信息,请阅读how或work。这称为按多个键排序,操作简单。如果使用的排序库函数使用比较器回调函数来决定两个元素的相对顺序,请定义比较器函数,以便它首先检查两个输入值a和b是否具有相等的isBig
值,如果没有,则立即返回a.isBig>b.isBig
(我在这里假设
是为布尔值定义的;如果不是,则替换明显的测试)。但是如果是大的
值相等,您应该返回a.timestamp>b.timestamp
,您可以定义一个自定义比较器,并使用它对列表进行排序
class ItemComparator implements Comparator {
@Override
public int compare (Item a, Item b) {
int bc = Boolean.compare(a.isBig, b.isBig);
if (bc != 0)
return bc;
return Long.compare(a.timestamp, b.timestamp);
}
}
像这样使用它
Collections.sort(list, ItemComparator);
理论上,使用两个单独列表的方法应该比使用两步比较器的方法快,因为基于一个字段的比较明显比基于两个字段的比较快。通过使用两个列表,可以加快算法中时间复杂度O(n log n)
的部分(排序),以牺牲额外的初始阶段(分为两部分)为代价,该阶段具有时间复杂性O(n)
。由于n log n>n
,对于非常非常大的n
值,两个列表方法应该更快
然而,在实践中,我们讨论的是时间上的微小差异,在两个列表方法胜出之前,您必须有非常长的列表,因此在您开始遇到诸如OutOfMemoryError
之类的问题之前,很难使用列表演示差异
但是,如果您使用数组而不是列表,并且使用巧妙的技巧而不是使用单独的数据结构,则有可能击败两步比较器
方法,如下代码所示。在任何人抱怨之前:是的,我知道这不是一个合适的基准
尽管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);
}
}
}
公共类主{
静态随机兰德=新随机();
静态复合兰德(){
返回新化合物(rand.nextBoolean(),rand.nextLong());
}
静态复合[]随机数组(){
整数长度=100_000;
化合物[]温度=新化合物[长度];
for(int i=0;i>32));
返回结果;
}
}
静态最终比较器比较器=新比较器(){
@凌驾
公共整数比较(化合物o1,化合物o2){
int结果=(o1.bool?0:1)-(o2.bool?0:1);
返回结果!=0?结果:Long.compare(o1.time,o2.time);
}
};
静态最终比较器LONG_ONLY_比较器=新比较器(){
@凌驾
公共整数比较(化合物o1,化合物o2){
返回长时间比较(o1.time,o2.time);
}
};
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);
}
}
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<BooleanComaprator> booleanComparator= new Comparator<BooleanComaprator>() {
@Override
public int compare(BooleanComaprator e1, BooleanComaprator e2) {
if (e1.isBig && !e2.isBig)
return -1;
if (!e1.isBig && e2.isBig)
return 1;
else
return 0;
}
}