比较器基于对象的不同可空字段

时间:2016-09-15 19:32:04

标签: java java-8 comparator

我有一个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

任何人都可以帮我解决这个问题吗?

2 个答案:

答案 0 :(得分:7)

由于您使用的是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

答案 1 :(得分:2)

如果标题的 null,则两个Employee将评估为等于,即使其中一个不为空。这不是你想要的。您希望所有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());
}

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