对一组对象进行排序

65

如果我有一个简单的字符串列表:

List<String> stringList = new ArrayList<String>();

我可以使用以下方式对其进行排序:

Collections.sort(stringList);

但是假设我有一个Person类:

public class Person
{
   private String name;
   private Integer age;
   private String country;
}

以下是相关列表:

List<Person> personList = new ArrayList<Person>();

我有时想按名称、有时按年龄、有时按国家对它进行排序。

有什么简单的方法可以实现这一点?

我知道可以实现Comparable接口,但那似乎限制了我只能按照一个特定属性进行排序。


2
也许你不应该在标题中使用形容词“复合的”,因为它听起来像是复合模式(Composite Pattern)... - fortran
9个回答

52

Collections.sort可以使用自定义比较器进行调用。该比较器可以实现允许以不同的排序顺序排序。这里是一个示例(针对您的Person模型-使用年龄作为整数):

public class FlexiblePersonComparator implements Comparator<Person> {
  public enum Order {Name, Age, Country}

  private Order sortingBy = Name;

  @Override
  public int compare(Person person1, Person person2) {
    switch(sortingBy) {
      case Name: return person1.name.compareTo(person2.name);
      case Age: return person1.age.compareTo(person2.age);
      case Country: return person1.country.compareTo(person2.country);
    }
    throw new RuntimeException("Practically unreachable code, can't be thrown");
  }

  public void setSortingBy(Order sortBy) {
    this.sortingBy = sortingBy;
  }
}

假设persons是一个字段,您可以像这样使用它:

public void sortPersonsBy(FlexiblePersonComparator.Order sortingBy) {
  List<Person> persons = this.persons;  // useless line, just for clarification
  FlexiblePersonComparator comparator = new FlexiblePersonComparator();
  comparator.setSortingBy(sortingBy);
  Collections.sort(persons, comparator); // now we have a sorted list
}

2
+1 我喜欢一个灵活的比较器的想法。 - dfa
5
你也可以在构造函数中传入sortingBy参数来实现排序。 - Knut Arne Vedaa
此帖中@Yishai的回答展示了枚举在自定义排序和分组排序(多个参数)中的优雅使用,利用比较器链接。 - gunalmel
这应该是被接受的答案。 - Dinesh Falwadiya

36

实现 Comparator 接口(每个不同的排序方式都需要实现一次),并使用将 Comparator 作为额外参数的 Collections.sort() 方法。


17

感谢回答者。为了让其他人受益,我想包含一个完整的示例。

解决方案是创建以下额外类:

public class NameComparator implements Comparator<Person>
{
    public int compare(Person o1, Person o2)
    {
       return o1.getName().compareTo(o2.getName());
   }
}

public class AgeComparator implements Comparator<Person>
{
    public int compare(Person o1, Person o2)
    {
        return o1.getAge().compareTo(o2.getAge());
    }
}

public class CountryComparator implements Comparator<Person>
{
    public int compare(Person o1, Person o2)
    {
        return o1.getCountry().compareTo(o2.getCountry());
    }
}

可以按以下方式对列表进行排序:

Collections.sort(personList, new NameComparator());
Collections.sort(personList, new AgeComparator());
Collections.sort(personList, new CountryComparator());

12

使用Java 8的方法是采用List.sort,示例如下:

personList.sort(Comparator.comparing(Person::getName));

引用Stuart Marks这里的回答中的话。

List.sort(cmp)扩展方法相较于Collections.sort(list, cmp)有一个重大优势。可能看起来只是一种小的语法优势,可以编写myList.sort(cmp)而不是Collections.sort(myList, cmp)。区别在于myList.sort(cmp)是一种接口扩展方法,可以被特定的List实现覆盖重写。例如,ArrayList.sort(cmp)使用Arrays.sort()原地对列表进行排序,而默认实现则实现了旧的复制-排序-复制回技术。


6
您也可以使用来自Apache Commons BeanUtils的BeanComparator,如下所示:

BeanComparator

Collections.sort(personList, new BeanComparator("name"));

不错啊。以前没见过。 - Lawrence Tierney

4

实现3种不同类型的比较器。

您可以将比较器添加到排序命令中。您定义的比较器将按名称、年龄或其他方式对元素进行排序。

Collections.sort(list, new Comparator() {

        public int compare(Object arg0, Object arg1) {
            if (!(arg0 instanceof Person)) {
                return -1;
            }
            if (!(arg1 instanceof Person)) {
                return -1;
            }

            Person pers0 = (Person)arg0;
            Person pers1 = (Person)arg1;


            // COMPARE NOW WHAT YOU WANT
            // Thanks to Steve Kuo for your comment!
            return pers0.getAge() - pers1.getAge();
        }
    });

为什么不使用参数化类型来实现Comparator? - dfa
2
因为我来自1.4版本 ;) - Markus Lausberg
2
你可以只返回pers0.getAge() - pers1.getAge()。这对于三种情况(<,>和==)都有效。 - Steve Kuo

2

Collections.sort方法可以使用第二个参数来指定要使用的比较器。 创建3个比较器,并在适当的时候使用所需的比较器。

Collections.sort(list , new Comparator() {
        public int compare(Object o1, Object o2) {
          ...
        }
      });

2
哎呀呀...Comparator<Person>...语法糖真好吃! - basszero
是的,我也这么认为。应该是 Collections.sort(Person , new Comparator<Person>() { - roottraveller

1

使用lambdaj(http://code.google.com/p/lambdaj/),您可以按以下方式实现您所要求的内容:

sort(personList, on(Person.class).getName());

sort(personList, on(Person.class).getAge());

sort(personList, on(Person.class).getCountry());


0

我曾经问过一个非常相似的问题(关于搜索而不是排序),也许有一些有用的信息(最终我使用了实现Comparatorenum,因此我将enum值作为比较器选择器传递)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接