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

不羁的心 提交于 2021-02-10 05:33:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!