如何在Java中按两个字段排序?

Dam*_*mir 152 java sorting

我有一些对象person (int age; String name;).

如何按名称和年龄按字母顺序对此数组进行排序?

您会使用哪种算法?

Ric*_*d H 199

您可以使用Collections.sort如下:

private static void order(List<Person> persons) {

    Collections.sort(persons, new Comparator() {

        public int compare(Object o1, Object o2) {

            String x1 = ((Person) o1).getName();
            String x2 = ((Person) o2).getName();
            int sComp = x1.compareTo(x2);

            if (sComp != 0) {
               return sComp;
            } 

            Integer x1 = ((Person) o1).getAge();
            Integer x2 = ((Person) o2).getAge();
            return x1.compareTo(x2);
    }});
}
Run Code Online (Sandbox Code Playgroud)

List<Persons> 现在按名称排序,然后按年龄排序.

String.compareTo"按字典顺序比较两个字符串" - 来自文档.

Collections.sort是本机Collections库中的静态方法.它进行实际的排序,你只需要提供一个Comparator来定义列表中两个元素的比较方式:这是通过提供你自己的compare方法实现来实现的.

  • 由于这个问题/答案仍然存在联系,请注意,使用Java SE 8时,这变得更加简单.如果有getter你可以写`Comparator <Person> comparator = Comparator.comparing(Person :: getName).thenComparingInt(Person :: getAge);` (15认同)
  • 您还可以向`Comparator`添加一个类型参数,以避免必须转换输入. (9认同)

Luk*_*ski 129

对于那些能够使用Java 8流API的人来说,这里有一个更为简洁的方法: Lambdas和排序

我正在寻找相当于C#LINQ:

.ThenBy(...)
Run Code Online (Sandbox Code Playgroud)

我在Comparator上找到了Java 8中的机制:

.thenComparing(...)
Run Code Online (Sandbox Code Playgroud)

所以这里是演示算法的片段.

    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Run Code Online (Sandbox Code Playgroud)

查看上面的链接,了解更简洁的方法以及有关Java类型推断如何使其与LINQ相比更加笨拙的解释.

以下是完整的单元测试供参考:

@Test
public void testChainedSorting()
{
    // Create the collection of people:
    ArrayList<Person> people = new ArrayList<>();
    people.add(new Person("Dan", 4));
    people.add(new Person("Andi", 2));
    people.add(new Person("Bob", 42));
    people.add(new Person("Debby", 3));
    people.add(new Person("Bob", 72));
    people.add(new Person("Barry", 20));
    people.add(new Person("Cathy", 40));
    people.add(new Person("Bob", 40));
    people.add(new Person("Barry", 50));

    // Define chained comparators:
    // Great article explaining this and how to make it even neater:
    // http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
    Comparator<Person> comparator = Comparator.comparing(person -> person.name);
    comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));

    // Sort the stream:
    Stream<Person> personStream = people.stream().sorted(comparator);

    // Make sure that the output is as expected:
    List<Person> sortedPeople = personStream.collect(Collectors.toList());
    Assert.assertEquals("Andi",  sortedPeople.get(0).name); Assert.assertEquals(2,  sortedPeople.get(0).age);
    Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
    Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
    Assert.assertEquals("Bob",   sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
    Assert.assertEquals("Bob",   sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
    Assert.assertEquals("Bob",   sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
    Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
    Assert.assertEquals("Dan",   sortedPeople.get(7).name); Assert.assertEquals(4,  sortedPeople.get(7).age);
    Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3,  sortedPeople.get(8).age);
    // Andi     : 2
    // Barry    : 20
    // Barry    : 50
    // Bob      : 40
    // Bob      : 42
    // Bob      : 72
    // Cathy    : 40
    // Dan      : 4
    // Debby    : 3
}

/**
 * A person in our system.
 */
public static class Person
{
    /**
     * Creates a new person.
     * @param name The name of the person.
     * @param age The age of the person.
     */
    public Person(String name, int age)
    {
        this.age = age;
        this.name = name;
    }

    /**
     * The name of the person.
     */
    public String name;

    /**
     * The age of the person.
     */
    public int age;

    @Override
    public String toString()
    {
        if (name == null) return super.toString();
        else return String.format("%s : %d", this.name, this.age);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果有getter你可以写`Comparator <Person> comparator = Comparator.comparing(Person :: getName).thenComparing(Person :: getAge);` (15认同)
  • 使用 `thenComparingInt` 表示年龄 (int) (2认同)

Bra*_*y D 95

使用Java 8 Streams方法......

//Creates and sorts a stream (does not sort the original list)       
persons.stream().sorted(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Run Code Online (Sandbox Code Playgroud)

而Java 8 Lambda方法......

//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
        if (p1.getName().compareTo(p2.getName()) == 0) {
            return p1.getAge().compareTo(p2.getAge());
        } else {
            return p1.getName().compareTo(p2.getName());
        } 
    });
Run Code Online (Sandbox Code Playgroud)

最后...

//This is similar SYNTAX to the Streams above, but it sorts the original list!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Run Code Online (Sandbox Code Playgroud)


Ral*_*lph 17

您需要实现自己的Comparator,然后使用它:例如

Arrays.sort(persons, new PersonComparator());
Run Code Online (Sandbox Code Playgroud)

您的比较器可能看起来像这样:

public class PersonComparator implements Comparator<? extends Person> {

  public int compare(Person p1, Person p2) {
     int nameCompare = p1.name.compareToIgnoreCase(p2.name);
     if (nameCompare != 0) {
        return nameCompare;
     } else {
       return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

比较器首先比较名称,如果它们不相等则返回比较它们的结果,否则它在比较两个人的年龄时返回比较结果.

这段代码只是草稿:因为类是不可变的,你可以考虑构建它的单例,而不是为每个排序创建一个新实例.


Mic*_*rry 13

让你的person类实现Comparable<Person>然后实现compareTo方法,例如:

public int compareTo(Person o) {
    int result = name.compareToIgnoreCase(o.name);
    if(result==0) {
        return Integer.valueOf(age).compareTo(o.age);
    }
    else {
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

这将首先按名称排序(不区分大小写)然后按年龄排序.然后,您可以运行Arrays.sort()Collections.sort()在Person对象的集合或数组.


小智 10

您可以使用Java 8 Lambda方法来实现。像这样:

persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Run Code Online (Sandbox Code Playgroud)


Pri*_*tre 6

GuavaComparisonChain提供了一种干净的方法。请参阅此链接

用于执行链接比较语句的实用程序。例如:

   public int compareTo(Foo that) {
     return ComparisonChain.start()
         .compare(this.aString, that.aString)
         .compare(this.anInt, that.anInt)
         .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
         .result();
   }
Run Code Online (Sandbox Code Playgroud)


raf*_*bbr 6

你可以这样做:

List<User> users = Lists.newArrayList(
  new User("Pedro", 12), 
  new User("Maria", 10), 
  new User("Rafael",12)
);

users.sort(
  Comparator.comparing(User::getName).thenComparing(User::getAge)
);
Run Code Online (Sandbox Code Playgroud)


Gui*_*ina 5

使用 Guava 时我会小心,ComparisonChain因为它会为每个比较的元素创建一个实例,因此您将查看N x Log N比较链的创建,以便在排序时进行比较,或者N在迭代和检查是否相等时查看实例。

Comparator如果可能的话,我会使用最新的 Java 8 API 或允许您执行此操作的 Guava 的API创建静态Ordering,这是 Java 8 的示例:

import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;

private static final Comparator<Person> COMPARATOR = Comparator
  .comparing(Person::getName, nullsLast(naturalOrder()))
  .thenComparingInt(Person::getAge);

@Override
public int compareTo(@NotNull Person other) {
  return COMPARATOR.compare(this, other);
}
Run Code Online (Sandbox Code Playgroud)

以下是如何使用 Guava 的OrderingAPI:https://github.com/google/guava/wiki/OrderingExplained