Java中如何基于比较器执行排序保持原始排序的完整性

Java中如何基于比较器执行排序保持原始排序的完整性,java,sorting,collections,comparator,comparable,Java,Sorting,Collections,Comparator,Comparable,我已经介绍了Comparable vs Comparator接口的实现示例 但是,我在it的实施过程中遇到了一个问题: 假设我有一个简单的类:Employee,它有一个基于雇员姓名的默认排序机制 public class Employee implements Comparable<Employee> { private int empSalary; private String empName; @Override public int compareT

我已经介绍了Comparable vs Comparator接口的实现示例

但是,我在it的实施过程中遇到了一个问题:

假设我有一个简单的类:Employee,它有一个基于雇员姓名的默认排序机制

public class Employee implements Comparable<Employee> {

   private int empSalary;
   private String empName;

   @Override
   public int compareTo(Employee e) {
        return this.empName.compareTo(e.empName);
   }

}
输入顺序:

Name : Kumar, Salary : 40
Name : Sanket, Salary : 10
Name : Kumar, Salary : 20
预期产出:

Name : Kumar, Salary : 20
Name : Kumar, Salary : 40
Name : Sanket, Salary : 10
实际产量:

Name : Sanket, Salary : 10 // incorrect order
Name : Kumar, Salary : 20
Name : Kumar, Salary : 40

这并不是因为您的
Employee
类已经有了默认的排序,即使用
Collections.sort
和自定义比较器将引入一个新的排序层

例如,假设您的
员工的默认顺序是按工资升序排列的。现在让我们假设您想按工资降序对它们进行排序

根据你的逻辑,这将如何表现

Collections.sort(employees, new SalaryDescendingComparator());
事实上,当您为
Collections.sort
提供自定义比较器时,它将仅使用此比较器,而不使用您在
Employee
类中实现的排序机制

作为缔约国:

根据 指定比较器

因为
SalaryComparator
只根据员工的工资对员工进行比较,所以您会得到这个输出

如果您想先按姓名排序,然后按工资排序,则必须一次性完成,即:

public class Employee implements Comparable<Employee> {

   private int empSalary;
   private String empName;

   @Override
   public int compareTo(Employee e) {
       int cmp = this.empName.compareTo(e.empName);
       return cmp != 0 ? cmp : Integer.compare(empSalary, e.empSalary);
   }

}
public类Employee实现了可比较的{
私人薪酬;
私有字符串名称;
@凌驾
公共内部比较(员工e){
int cmp=this.empName.compareTo(e.empName);
返回cmp!=0?cmp:整数。比较(empSalary,e.empSalary);
}
}

这并不是因为您的
员工
类已经有了默认的排序,即使用
集合。使用自定义比较器进行排序将引入一个新的排序层

例如,假设您的
员工的默认顺序是按工资升序排列的。现在让我们假设您想按工资降序对它们进行排序

根据你的逻辑,这将如何表现

Collections.sort(employees, new SalaryDescendingComparator());
事实上,当您为
Collections.sort
提供自定义比较器时,它将仅使用此比较器,而不使用您在
Employee
类中实现的排序机制

作为缔约国:

根据 指定比较器

因为
SalaryComparator
只根据员工的工资对员工进行比较,所以您会得到这个输出

如果您想先按姓名排序,然后按工资排序,则必须一次性完成,即:

public class Employee implements Comparable<Employee> {

   private int empSalary;
   private String empName;

   @Override
   public int compareTo(Employee e) {
       int cmp = this.empName.compareTo(e.empName);
       return cmp != 0 ? cmp : Integer.compare(empSalary, e.empSalary);
   }

}
public类Employee实现了可比较的{
私人薪酬;
私有字符串名称;
@凌驾
公共内部比较(员工e){
int cmp=this.empName.compareTo(e.empName);
返回cmp!=0?cmp:整数。比较(empSalary,e.empSalary);
}
}

如果您想创建一个自定义的比较器来根据薪资区分姓名关系,请比较器首先尝试比较两名员工,然后如果这是一个关系,请使用薪资:

public class SalaryComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee e1, Employee e2)  {
        if (e1.compareTo(e2) == 0)
            return e1.empSalary - e2.empSalary;
        return e1.compareTo(e2);
    }
}
公共类SalaryComparator实现Comparator{
@凌驾
公共整数比较(员工e1、员工e2){
如果(e1.与(e2)==0相比)
返回e1.empSalary-e2.empSalary;
返回e1.compareTo(e2);
}
}

如果您想创建一个自定义的比较器来根据薪资区分姓名关系,请比较器首先尝试比较两名员工,然后如果这是一个关系,请使用薪资:

public class SalaryComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee e1, Employee e2)  {
        if (e1.compareTo(e2) == 0)
            return e1.empSalary - e2.empSalary;
        return e1.compareTo(e2);
    }
}
公共类SalaryComparator实现Comparator{
@凌驾
公共整数比较(员工e1、员工e2){
如果(e1.与(e2)==0相比)
返回e1.empSalary-e2.empSalary;
返回e1.compareTo(e2);
}
}

请注意,在Java 8中,您可以通过以下方式执行此操作:

public class Employee {
    private int empSalary;
    private String empName;

    public int getEmpSalary() {
        return empSalary;
    }

    public String getEmpName() {
        return empName;
    }
}
请注意,我添加了一个公共getter,然后对于简单比较

List<Employee> employeeList = new ArrayList<>();
// Populate it
employeeList.sort(Comparator.comparingInt(Employee::getEmpSalary));
现在,如果要首先比较姓名和薪资,可以使用:

employeeList.sort(
        Comparator.comparing(Employee::getEmpName)
        .thenComparingInt(Employee::getEmpSalary)

请注意,在Java 8中,您可以通过以下方式执行此操作:

public class Employee {
    private int empSalary;
    private String empName;

    public int getEmpSalary() {
        return empSalary;
    }

    public String getEmpName() {
        return empName;
    }
}
请注意,我添加了一个公共getter,然后对于简单比较

List<Employee> employeeList = new ArrayList<>();
// Populate it
employeeList.sort(Comparator.comparingInt(Employee::getEmpSalary));
现在,如果要首先比较姓名和薪资,可以使用:

employeeList.sort(
        Comparator.comparing(Employee::getEmpName)
        .thenComparingInt(Employee::getEmpSalary)

不像其他人建议的那样,创建一个包含所有条件的新比较器,您还可以多次排序

因此,如果您将排序部件替换为:

Collections.sort(employeeList, new SalaryComparator());
Collections.sort(employeeList);
然后您将获得预期的输出

与使用组合比较器同时对所有内容进行排序相比,它的效率可能更低,但当需要使用多个属性进行排序时,它非常方便

如果您真的需要一种强大的方法来按不同属性排序(如在表中),那么您应该尝试创建一个比较器链。
下面是这种模式的一个示例:


(这是使用Java8:)执行答案中建议的操作的旧方法)

您也可以多次排序,而不是像其他人建议的那样创建一个包含所有条件的新比较器

因此,如果您将排序部件替换为:

Collections.sort(employeeList, new SalaryComparator());
Collections.sort(employeeList);
然后您将获得预期的输出

与使用组合比较器同时对所有内容进行排序相比,它的效率可能更低,但当需要使用多个属性进行排序时,它非常方便

如果您真的需要一种强大的方法来按不同属性排序(如在表中),那么您应该尝试创建一个比较器链。
下面是这种模式的一个示例:

(这是使用Java8执行答案中建议的操作的老方法:)