假设你有一些对象,它们有几个字段可以比较:

public class Person {

    private String firstName;
    private String lastName;
    private String age;

    /* Constructors */

    /* Methods */

}

所以在这个例子中,当你问if:

a.compareTo(b) > 0

你可能会问a的姓是不是在b的姓之前,或者a的年龄是不是比b大,等等……

在不增加不必要的混乱或开销的情况下,在这些类型的对象之间进行多重比较的最干净的方法是什么?

comparable接口只允许通过一个字段进行比较 在我看来,添加大量的比较方法(如compareByFirstName(), compareByAge()等)是混乱的。

那么最好的解决办法是什么呢?


当前回答

晚做总比不到好——如果你正在寻找不必要的混乱或开销,那么很难同时在最少的代码/快速执行方面击败下面的方法。

数据类:

public class MyData {
    int id;
    boolean relevant;
    String name;
    float value;
}

比较器:

public class MultiFieldComparator implements Comparator<MyData> {
    @Override
    public int compare(MyData dataA, MyData dataB) {
        int result;
        if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
           (result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
           (result = dataA.name.compareTo(dataB.name)) == 0)
            result = Float.compare(dataA.value, dataB.value);
        return result;
    }
}

如果你只是想按自定义顺序对集合进行排序,那么下面的代码就更清晰了:

myDataList.sort((dataA, dataB) -> {
    int result;
    if((result = Integer.compare(dataA.id, dataB.id)) == 0 &&
       (result = Boolean.compare(dataA.relevant, dataB.relevant)) == 0 &&
       (result = dataA.name.compareTo(dataB.name)) == 0)
        result = Float.compare(dataA.value, dataB.value);
    return result;
});

其他回答

Java 8通过lambda方式我们可以通过方法引用进行比较。 学生POJO

public class Student {
int id;
String firstName;
String lastName;
String subject;

public Student(int id, String firstName, String lastName, String subject) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.subject = subject;
}
enter code here

现在我们可以根据

1. id - > FirstName - > LastName - > 2。主题- > id - > FirstName - > LastName

我们将在数组Stream中使用Comparator

public class TestComprator {
public static void main(String[] args) {
    Student s1= new Student(108, "James", "Testo", "Physics");
    Student s2= new Student(101, "Fundu", "Barito", "Chem");
    Student s3= new Student(105, "Sindhu", "Sharan", "Math");
    Student s4= new Student(98, "Rechel", "Stephen", "Physics");
    System.out.printf("----------id->FirstName->LastName->Subject-------------");
    Arrays.asList(s1,s2,s3,s4).stream()
            .sorted(Comparator.comparing(Student::getId)
                    .thenComparing(Student::getFirstName)
                .thenComparing(Student::getLastName)
                .thenComparing(Student::getSubject))
            .forEach(System.out::println);

    System.out.printf("----Subject->id->FirstName->LastName ------\n");
    Arrays.asList(s1,s2,s3,s4).stream()
            .sorted(Comparator. comparing(Student::getSubject)
                    .thenComparing(Student::getId)
                    .thenComparing(Student::getFirstName)
                    .thenComparing(Student::getLastName)
                   )
            .forEach(System.out::println);
}

}

输出:

`----------id->FirstName->LastName->Subject-------------
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}
 ----Subject->id->FirstName->LastName ------
Student{id=101, firstName='Fundu', lastName='Barito', subject='Chem'}
Student{id=105, firstName='Sindhu', lastName='Sharan', subject='Math'}
Student{id=98, firstName='Rechel', lastName='Stephen', subject='Physics'}
Student{id=108, firstName='James', lastName='Testo', subject='Physics'}

您可以实现一个Comparator来比较两个Person对象,并且可以检查任意数量的字段。你可以在比较器中放入一个变量,告诉它与哪个字段进行比较,尽管只编写多个比较器可能会更简单。

另一个可以考虑的选项是Apache Commons。它提供了很多选项。

import org.apache.commons.lang3.builder.CompareToBuilder;

Ex:

public int compare(Person a, Person b){

   return new CompareToBuilder()
     .append(a.getName(), b.getName())
     .append(a.getAddress(), b.getAddress())
     .toComparison();
}
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

class User {
    private String firstName;
    private String lastName;
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

}

public class MultiFieldsComparision {

    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();

        User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
        User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
        User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
        User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
        User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
        User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);

        users.add(u1);
        users.add(u2);
        users.add(u3);
        users.add(u4);
        users.add(u5);
        users.add(u6);

        System.out.println("****** Before Sorting ******");

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

        System.out.println("****** Aftre Sorting ******");

        users.sort(
                Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));

        users.forEach(user -> {
            System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
        });

    }

}

要连续排序多个字段,请尝试ComparatorChain

A ComparatorChain is a Comparator that wraps one or more Comparators in sequence. The ComparatorChain calls each Comparator in sequence until either 1) any single Comparator returns a non-zero result (and that result is then returned), or 2) the ComparatorChain is exhausted (and zero is returned). This type of sorting is very similar to multi-column sorting in SQL, and this class allows Java classes to emulate that kind of behaviour when sorting a List. To further facilitate SQL-like sorting, the order of any single Comparator in the list can >be reversed. Calling a method that adds new Comparators or changes the ascend/descend sort after compare(Object, Object) has been called will result in an UnsupportedOperationException. However, take care to not alter the underlying List of Comparators or the BitSet that defines the sort order. Instances of ComparatorChain are not synchronized. The class is not thread-safe at construction time, but it is thread-safe to perform multiple comparisons after all the setup operations are complete.