简体   繁体   中英

How is the rule for sorting a list in ascending order and descending order?

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);
    }
}

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);
}

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]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM