【问题标题】:Comparator based on different nullable fields of an object基于对象的不同可为空字段的比较器
【发布时间】:2016-09-15 19:32:03
【问题描述】:

我有一个Employee 对象,其中包含两个字段namejobTitle。对于员工对象的排序,优先级应为jobTitle,如果jobTitle为空,则排序应基于名称。

下面是Employee对象

public class Employee {
    private String name;
    private String jobTitle;
}

我使用带有JobTitlecomparatorNameComparator 的链式比较器来实现这一点:

public class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    @SafeVarargs
    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
        this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee emp1, Employee emp2) {
        for (Comparator<Employee> comparator : listComparators) {
            int result = comparator.compare(emp1, emp2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

public class EmployeeJobTitleComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee emp1, Employee emp2) {
        if(emp1.getJobTitle() != null && emp2.getJobTitle() != null){
            return emp1.getJobTitle().compareTo(emp2.getJobTitle());
        } else {
            return 0;
        }
    }
}

public class EmployeeNameComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee emp1, Employee emp2) {
        return emp1.getName().compareTo(emp2.getName());
    }
}

public class SortingMultipleAttributesExample {
    public static void main(String[] args) {
        List<Employee> listEmployees = new ArrayList<Employee>();
        listEmployees.add(new Employee("Tom", "Developer"));
        listEmployees.add(new Employee("Sam", null));
        listEmployees.add(new Employee("Tim", "Designer"));
        listEmployees.add(new Employee("Bob", null));
        listEmployees.add(new Employee("Peter", null));
        listEmployees.add(new Employee("Craig", "Programmer"));

        Collections.sort(listEmployees, new EmployeeChainedComparator(new EmployeeJobTitleComparator(), new EmployeeNameComparator()
                ));

        for(Employee emp : listEmployees){
            System.out.println("Employee Job: "+emp.getJobTitle()+" Employee Name: "+emp.getName());
        }
    }
}

现在我应该得到这样的输出

Employee Job: Designer Employee Name: Tim
Employee Job: Developer Employee Name: Tom
Employee Job: Programmer Employee Name: Craig
Employee Job: null Employee Name: Bob
Employee Job: null Employee Name: Peter
Employee Job null Employee Name: Sam

但我没有得到预期的结果。我得到这样的输出

Employee Job Developer Employee Name Tom
Employee Job null Employee Name Sam
Employee Job Designer Employee Name Tim
Employee Job null Employee Name Bob
Employee Job null Employee Name Peter
Employee Job Programmer Employee Name Craig

谁能帮我解决这个问题?

【问题讨论】:

  • 你得到了什么输出?
  • 得到这样的输出 Employee Job Developer Employee Name Tom Employee Job null Employee Name Sam Employee Job Designer Employee Name Tim Employee Job null Employee Name Bob Employee Job null Employee Name Peter Employee Job Programmer Employee Name Craig
  • 不确定这是否是整个问题,但调用EmployeeChainedComparator 的构造函数只会传递EmployeeJobTitleComparator。尝试添加EmployeeNameComparator,使其包含在列表中。
  • @AndrewS 我刚刚通过传递 NameConstructor 更新了我的问题

标签: java java-8 comparator


【解决方案1】:

由于您使用的是 Java 8,因此您可以使用内置的比较器工具,而不是创建自己的比较器。比较职称和名字就可以轻松搞定

Comparator<Employee> comparator =
     Comparator.comparing(Employee::getJobTitle).thenComparing(Employee:getName);

nullsLastnullsFirst 方法也内置了如何处理 null 值。这些方法将现有比较器包装到 null 安全比较器中,将 null 值放在末尾或开头。

因此,您可以:

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

// ...

Comparator<Employee> comparator = 
    comparing(Employee::getJobTitle, nullsLast(naturalOrder())).thenComparing(Employee::getName);

Collections.sort(listEmployees, comparator);

比较器是由comparing 职位名称创建的,其中null 安全比较器将null 值放在最后(see also)。对于相同的头衔,它是thenComparing 员工的姓名。

【讨论】:

    【解决方案2】:

    如果其中一个标题是null,那么这两个Employees 将评估为相等,即使其中一个不为空。那不是你想要的。您希望所有 null 标题彼此相等,但不是非空值。

    用这个替换你的比较方法:

    public int compare(Employee emp1, Employee emp2) {
        if(emp1.getJobTitle() == null && emp2.getJobTitle() == null){
            return 0;
        }
        if(emp1.getJobTitle() == null) return 1;
        if(emp2.getJobTitle() == null) return -1;
        return emp1.getJobTitle().compareTo(emp2.getJobTitle());
    }
    

    你应该得到你期望的结果。

    【讨论】:

    • 感谢@resueman,我用上面的代码得到了预期的输出
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多