使用比较器降序排序(用户定义的类)

我想使用比较器按降序对我的对象进行排序。

class Person {
private int age;
}

这里我想对 Person 对象的数组进行排序。

我怎么能这么做?

317503 次浏览
String[] s = {"a", "x", "y"};
Arrays.sort(s, new Comparator<String>() {


@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
System.out.println(Arrays.toString(s));


-> [y, x, a]

Now you have to implement the Comparator for your Person class. Something like (for ascending order): compare(Person a, Person b) = a.id < b.id ? -1 : (a.id == b.id) ? 0 : 1 or Integer.valueOf(a.id).compareTo(Integer.valueOf(b.id)).

To minimize confusion you should implement an ascending Comparator and convert it to a descending one with a wrapper (like this) new ReverseComparator<Person>(new PersonComparator()).

The java.util.Collections class has a sort method that takes a list and a custom Comparator. You can define your own Comparator to sort your Person object however you like.

package com.test;


import java.util.Arrays;


public class Person implements Comparable {


private int age;


private Person(int age) {
super();
this.age = age;
}


public int getAge() {
return age;
}


public void setAge(int age) {
this.age = age;
}


@Override
public int compareTo(Object o) {
Person other = (Person)o;
if (this == other)
return 0;
if (this.age < other.age) return 1;
else if (this.age == other.age) return 0;
else return -1;


}


public static void main(String[] args) {


Person[] arr = new Person[4];
arr[0] = new Person(50);
arr[1] = new Person(20);
arr[2] = new Person(10);
arr[3] = new Person(90);


Arrays.sort(arr);


for (int i=0; i < arr.length; i++ ) {
System.out.println(arr[i].age);
}
}


}

Here is one way of doing it.

I would create a comparator for the person class that can be parametrized with a certain sorting behaviour. Here I can set the sorting order but it can be modified to allow sorting for other person attributes as well.

public class PersonComparator implements Comparator<Person> {


public enum SortOrder {ASCENDING, DESCENDING}


private SortOrder sortOrder;


public PersonComparator(SortOrder sortOrder) {
this.sortOrder = sortOrder;
}


@Override
public int compare(Person person1, Person person2) {
Integer age1 = person1.getAge();
Integer age2 = person2.getAge();
int compare = Math.signum(age1.compareTo(age2));


if (sortOrder == ASCENDING) {
return compare;
} else {
return compare * (-1);
}
}
}

(hope it compiles now, I have no IDE or JDK at hand, coded 'blind')

Edit

Thanks to Thomas, edited the code. I wouldn't say that the usage of Math.signum is good, performant, effective, but I'd like to keep it as a reminder, that the compareTo method can return any integer and multiplying by (-1) will fail if the implementation returns Integer.MIN_INTEGER... And I removed the setter because it's cheap enough to construct a new PersonComparator just when it's needed.

But I keep the boxing because it shows that I rely on an existing Comparable implementation. Could have done something like Comparable<Integer> age1 = new Integer(person1.getAge()); but that looked too ugly. The idea was to show a pattern which could easily be adapted to other Person attributes, like name, birthday as Date and so on.

Using Google Collections:

class Person {
private int age;


public static Function<Person, Integer> GET_AGE =
new Function<Person, Integer> {
public Integer apply(Person p) { return p.age; }
};


}


public static void main(String[] args) {
ArrayList<Person> people;
// Populate the list...


Collections.sort(people, Ordering.natural().onResultOf(Person.GET_AGE).reverse());
}

For whats its worth here is my standard answer. The only thing new here is that is uses the Collections.reverseOrder(). Plus it puts all suggestions into one example:

/*
**  Use the Collections API to sort a List for you.
**
**  When your class has a "natural" sort order you can implement
**  the Comparable interface.
**
**  You can use an alternate sort order when you implement
**  a Comparator for your class.
*/
import java.util.*;


public class Person implements Comparable<Person>
{
String name;
int age;


public Person(String name, int age)
{
this.name = name;
this.age = age;
}


public String getName()
{
return name;
}


public int getAge()
{
return age;
}


public String toString()
{
return name + " : " + age;
}


/*
**  Implement the natural order for this class
*/
public int compareTo(Person p)
{
return getName().compareTo(p.getName());
}


static class AgeComparator implements Comparator<Person>
{
public int compare(Person p1, Person p2)
{
int age1 = p1.getAge();
int age2 = p2.getAge();


if (age1 == age2)
return 0;
else if (age1 > age2)
return 1;
else
return -1;
}
}


public static void main(String[] args)
{
List<Person> people = new ArrayList<Person>();
people.add( new Person("Homer", 38) );
people.add( new Person("Marge", 35) );
people.add( new Person("Bart", 15) );
people.add( new Person("Lisa", 13) );


// Sort by natural order


Collections.sort(people);
System.out.println("Sort by Natural order");
System.out.println("\t" + people);


// Sort by reverse natural order


Collections.sort(people, Collections.reverseOrder());
System.out.println("Sort by reverse natural order");
System.out.println("\t" + people);


//  Use a Comparator to sort by age


Collections.sort(people, new Person.AgeComparator());
System.out.println("Sort using Age Comparator");
System.out.println("\t" + people);


//  Use a Comparator to sort by descending age


Collections.sort(people,
Collections.reverseOrder(new Person.AgeComparator()));
System.out.println("Sort using Reverse Age Comparator");
System.out.println("\t" + people);
}
}

You can do the descending sort of a user-defined class this way overriding the compare() method,

Collections.sort(unsortedList,new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return b.getName().compareTo(a.getName());
}
});

Or by using Collection.reverse() to sort descending as user Prince mentioned in his comment.

And you can do the ascending sort like this,

Collections.sort(unsortedList,new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
});

Replace the above code with a Lambda expression(Java 8 onwards) we get concise:

Collections.sort(personList, (Person a, Person b) -> b.getName().compareTo(a.getName()));

As of Java 8, List has sort() method which takes Comparator as parameter(more concise) :

personList.sort((a,b)->b.getName().compareTo(a.getName()));

Here a and b are inferred as Person type by lambda expression.