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

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()等)是混乱的。

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


当前回答

您应该实现Comparable <Person>。假设所有字段都不为空(为了简单起见),年龄是int型,比较排名是第一,最后,年龄,compareTo方法非常简单:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

其他回答

您应该实现Comparable <Person>。假设所有字段都不为空(为了简单起见),年龄是int型,比较排名是第一,最后,年龄,compareTo方法非常简单:

public int compareTo(Person other) {
    int i = firstName.compareTo(other.firstName);
    if (i != 0) return i;

    i = lastName.compareTo(other.lastName);
    if (i != 0) return i;

    return Integer.compare(age, other.age);
}

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

数据类:

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;
});

如果实现Comparable接口,则需要选择一个简单的属性进行排序。这就是所谓的自然排序。把它看作默认值。通常在没有提供特定比较器时使用。通常这是名称,但您的用例可能调用不同的东西。您可以自由地使用任何数量的其他比较器,您可以提供给各种集合api来覆盖自然排序。

还要注意,通常如果a.c omareto (b) == 0,则a.c omareto (b) == true。如果没有也没关系,但是有副作用要注意。在Comparable接口上查看优秀的javadocs,您将找到许多关于这方面的有用信息。

//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());
        });

    }

}

从Steve的回答开始,三元操作符可以使用:

public int compareTo(Person other) {
    int f = firstName.compareTo(other.firstName);
    int l = lastName.compareTo(other.lastName);
    return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}