问题
I have been trying to understand how sorting using Comparable works. I have class Activity implementing Comparable interface. I am trying to understand how activity.finish-this.finish; sorts in descending order and how this.finish-activity.finish sorts in ascending order. I am confused about what to use first for sorting in ascending and descending?
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, 0, 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);
}
}
回答1:
It's hacky. The contract for compareTo is that it returns less than 0
when the result of comparison is less, greater than 0
when greater and 0
when equal. It is not obvious what the code does. Instead, I would use Integer.compare(int, int) - something like
@Override
public int compareTo(Object o) {
return Integer.compare(this.finish, ((Activity) o).finish);
}
回答2:
The Answer by Elliott Frisch is correct. In addition, consider using Comparator
rather than Comparable
for your situation.
tl;dr
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
consistent with equals
An additional problem is that if you read the Javadoc for Comparable, you will see that generally it is best for the logic of compareTo
to be be constant with the logic of equals
.
But you want your comparing to be done only on the finish
member field. So you should write your equals
(and hashCode
) to also focus solely on finish
. But that would mean that your last two objects seen in the Question would be considered equal as they both share the value 9
for their finish
. I expect you do not consider those two objects to be equal.
Comparator
Defining a Comparator outside your class is likely more appropriate than making your Activity
class implement Comparable. We do just that in the rest of this Answer.
With Java 16, we can define your Activity
class as a record. The compiler implicitly creates the constructor, getters, equals
& hashCode
, and toString
. Using record
is not critical to this Answer, but makes the example code brief.
package work.basil.example;
public record Activity( int start , int finish )
{
}
Comparator.comparingInt
Rather than define your own implementation of the Comparator
interface, we can use the predefined comparator for comparing int values.
We use a method reference using the double COLON characters to name the method that should be used in retrieving an int from our objects being compared. That method reference is Activity :: finish
. In records the default getter method is named the same as the property name, without the get
prefix commonly seen à la JavaBeans. So simply finish
as method name, not getFinish
.
Comparator.comparingInt( Activity :: finish ) ) // Method reference from `Activity` being passed to `Comparator` built to compare `int` values.
Let's define your inputs using convenient List.of syntax.
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 ) ,
new Activity( 5 , 9 )
);
The list produced by that code is unmodifiable. We want to sort a list, so we need a modifiable list. Feed that unmodifiable list to the constructor of a modifiable implementation of List
such as ArrayList.
List < Activity > activitiesAscending = new ArrayList <>( activities );
Ask the utility method Collections.sort
to sort that new list. Pass a Comparator
as we showed earlier.
Collections.sort( activitiesAscending , Comparator.comparingInt( Activity :: finish ) );
For the opposite, in descending order with your 9
finishing objects first and your 2
finishing object last, we again call Collections.sort
. But we pass a different Comparator
. We pass our original comparingInt
comparator to the utility method Collections.reverseOrder
to produce a new comparator.
List < Activity > activitiesDescending = new ArrayList <>( activities );
Collections.sort( activitiesDescending , Collections.reverseOrder( Comparator.comparingInt( Activity :: finish ) ) );
Entire code example.
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 ) ,
new Activity( 5 , 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 );
When run.
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], Activity[start=5, finish=9]]
activitiesAscending = [Activity[start=1, finish=2], Activity[start=3, finish=4], Activity[start=5, finish=7], Activity[start=8, finish=9], Activity[start=5, finish=9], Activity[start=0, finish=10]]
activitiesDescending = [Activity[start=0, finish=10], Activity[start=8, finish=9], Activity[start=5, finish=9], Activity[start=5, finish=7], Activity[start=3, finish=4], Activity[start=1, finish=2]]
来源:https://stackoverflow.com/questions/66078749/how-is-the-rule-for-sorting-a-list-in-ascending-order-and-descending-order