问题描述
我一直试图了解使用 Comparable 进行排序的工作原理。我有类 Activity 实现 Comparable 接口。我想了解如何activity.finish-this.finish;按降序排序以及 this.finish-activity.finish 如何按升序排序。我对升序和降序排序首先使用什么感到困惑?
class Activity implements Comparable {
int start;
int finish;
public Activity(int start,int finish) {
this.start = start;
this.finish = finish;
}
@Override
public int compareto(Object o) {
Activity activity = (Activity) o;
return activity.finish-this.finish;
}
}
public class ActivitySelectionProblem {
public static void main(String[] args) {
int start[] = {1,3,5,8,5};
int finish[] = {2,4,10,7,9,9};
List<Activity> list = new ArrayList<>();
for(int i = 0 ; i < start.length ; i++){
list.add(new Activity(start[i],finish[i]));
}
Collections.sort(list);
}
}
解决方法
这很糟糕。 compareTo
的约定是比较结果小于时返回小于 0
,大于时大于 0
,相等时返回 0
。代码的作用并不明显。相反,我会使用 Integer.compare(int,int)
- 类似
@Override
public int compareTo(Object o) {
return Integer.compare(this.finish,((Activity) o).finish);
}
,
Answer by Elliott Frisch 是正确的。此外,请考虑根据您的情况使用 Comparator
而不是 Comparable
。
tl;博士
Collections.sort( // Utility method for sorting a `List`.
activitiesAscending,// A list copied from your original list of inputs.
Comparator.comparingInt( Activity :: finish ) // A predefined implementation of `Comparator` interface,built for comparing `int` values.
); // Results in the passed list being modified,being sorted according to the logic of the passed `Comparator`.
Collections.sort(
activitiesDescending,Collections.reverseOrder( // Reverses the sorting logic of the passed `Comparator` object.
Comparator.comparingInt( Activity :: finish ) // Same `Comparator` as seen above.
)
);
compareTo
与 equals
一致
另一个问题是,如果您阅读 Comparable
的 Javadoc,您会发现通常最好使 compareTo
的逻辑与 equals
的逻辑保持一致.
但您希望仅在 finish
成员字段上进行比较。所以你应该写你的 equals
(和 hashCode
)也只关注 finish
。但这意味着您在问题中看到的最后两个对象将被视为相等,因为它们的 9
共享值 finish
。我希望您不会认为这两个对象是相等的。
Comparator
在您的类之外定义 Comparator
可能比让您的 Activity
类实现 Comparable
更合适。我们在本回答的其余部分就是这样做的。
使用 Java 16,我们可以将您的 Activity
类定义为 record。编译器隐式创建构造函数、getter、equals
& hashCode
和 toString
。使用 record
对这个答案并不重要,但会使示例代码简短。
package work.basil.example;
public record Activity( int start,int finish )
{
}
Comparator.comparingInt
我们可以使用 predefined comparator for comparing int
values,而不是定义您自己的 Comparator
接口实现。
我们使用带有双 COLON 字符的 method reference 来命名应该用于从被比较的对象中检索 int 的方法。该方法引用是 Activity :: finish
。在记录中,默认的 getter 方法的名称与属性名称相同,没有在 JavaBeans 中常见的 get
前缀。因此,只需将 finish
作为方法名称,而不是 getFinish
。
Comparator.comparingInt( Activity :: finish ) ) // Method reference from `Activity` being passed to `Comparator` built to compare `int` values.
让我们使用方便的 List.of
语法定义您的输入。
List < Activity > activities =
List.of(
new Activity( 1,2 ),new Activity( 3,4 ),new Activity( 0,10 ),new Activity( 5,7 ),new Activity( 8,9 ),9 )
);
该代码生成的列表是 unmodifiable。我们想对一个列表进行排序,所以我们需要一个可修改的列表。将该不可修改的列表提供给 List
的可修改实现的构造函数,例如 ArrayList
。
List < Activity > activitiesAscending = new ArrayList <>( activities );
要求实用方法 Collections.sort
对该新列表进行排序。正如我们之前展示的,传递一个 Comparator
。
Collections.sort( activitiesAscending,Comparator.comparingInt( Activity :: finish ) );
相反,按照降序排列,先是 9
完成对象,最后是 2
完成对象,我们再次调用 Collections.sort
。但是我们传递了一个不同的 Comparator
。我们将原始的 comparingInt
比较器传递给实用方法 Collections.reverseOrder
以生成新的比较器。
List < Activity > activitiesDescending = new ArrayList <>( activities );
Collections.sort( activitiesDescending,Collections.reverseOrder( Comparator.comparingInt( Activity :: finish ) ) );
整个代码示例。
List < Activity > activities =
List.of(
new Activity( 1,9 )
);
List < Activity > activitiesAscending = new ArrayList <>( activities );
Collections.sort( activitiesAscending,Comparator.comparingInt( Activity :: finish ) );
List < Activity > activitiesDescending = new ArrayList <>( activities );
Collections.sort( activitiesDescending,Collections.reverseOrder( Comparator.comparingInt( Activity :: finish ) ) );
System.out.println( "activities = " + activities );
System.out.println( "activitiesAscending = " + activitiesAscending );
System.out.println( "activitiesDescending = " + activitiesDescending );
运行时。
activities = [Activity[start=1,finish=2],Activity[start=3,finish=4],Activity[start=0,finish=10],Activity[start=5,finish=7],Activity[start=8,finish=9],finish=9]]
activitiesAscending = [Activity[start=1,finish=10]]
activitiesDescending = [Activity[start=0,Activity[start=1,finish=2]]