如何使用实现相同接口的不同对象对列表进行排序?

时间:2016-04-24 20:10:19

标签: java sorting generics collections comparable

我有一个抽象类:

public abstract class Employee implements Comparable<Employee>{
  public Double getSalary();


    @Override
    public Double compareTo(final Employee employee2) {
    }
}

扩展此抽象类的两个类:

public class Manager {
  public Double getSalary(){}
}

public class Chef{
  public Double getSalary(){}
}

我想对它进行排序,以便列表中的所有Chef对象都在前面,然后在所有Chef对象之后,我希望Manager对象按降序排序,并以最高薪水排序。换句话说,对于Chef对象,我只希望将它们推到列表的前面,没有别的。对于Manager对象,我希望它们按薪水排序,并在Chef对象之后放在列表的末尾。

e.g

Manager manager1 = new Manager(40000);
Manager manager2 = new Manager(50000);
Manager manager3 = new Manager(60000);
Chef chef1 = new Chef(25000);
Chef chef2 = new Chef(25500);
Chef chef3 = new Chef(28000);
Chef chef4 = new Chef(29000);
List<Employee> employees = new ArrayList<Employee>();
employees.add(manager3);
employees.add(manager1);
employees.add(chef1);
employees.add(chef3);
employees.add(manager2);
employees.add(chef4);
employees.add(chef2);

排序后,列表中的元素可能是这样的:

0. chef1 = 25000 // Chef objects pushed to front but not sorted by salary
1. chef3 = 28000
2. chef4 = 29000
3. chef2 = 25500
4. manager3 = 60000 // Manager objects pushed to the back and sorted by salary
5. manager2 = 50000
6. manager1 = 40000

我的compareTo方法:

@Override
public int compareTo(final Employee employee2) {
    if (this instanceof Manager|| employee2instanceof Manager) {
        return employee2.getSalary().compareTo(this.getSalary());
    }

    return -1;
}

但是,这会将Manager对象置于最前面。我对如何使用compareTo方法中的Chef对象感到困惑。

4 个答案:

答案 0 :(得分:4)

您的情况听起来像创建Comparator比实施Comparable更合适:后者专门用于建模类的实例自然顺序

例如,Integer implements Comparable<Integer>:整数有一个非常明显的自然排序。

您的案例听起来更像是一个关于如何对其进行排序的一次性案例,例如:在特定的报告中提出。

此外,我认为当父类需要了解其子类时,这是一种代码味道。没有什么可以阻止创建新的子类;没有必要更新Employee来处理这个事实。

很容易从一个转换为另一个:不是将int compare(Employee that)方法添加到Employee,而是在比较器中实现一个方法:

class ChefsAndManagersComparator implements Comparator<Employee> {
  @Override
  public int compare(Employee employee1, Employee employee2) {
    // The logic from any of the other answers,
    // just replacing "this" references with "employee1"
  }
}

答案 1 :(得分:1)

您可以使用3个列表,因此您无需检查特定的子类型:

List<Chef> chefs = new ArrayList<Chef>();
List<Manager> managers = new ArrayList<Manager>();

managers.add(manager3);
managers.add(manager1);
chefs.add(chef1);
chefs.add(chef3);
managers.add(manager2);
chefs.add(chef4);
chefs.add(chef2);

Collections.sort(managers);

List<Employee> employees = new ArrayList<Employee>();
employees.addAll(chefs);
employess.addAll(managers);

但是,如果您无法改变员工存储的实施方式,您仍然可以遍历员工列表,检查每种类型并插入特定列表。然后,在最后连接:

...
List<Chef> chefs = new ArrayList<Chef>();
List<Manager> managers = new ArrayList<Manager>();
List<Employee> others = new ArrayList<Employee>();

for(Employee e : employees) {
    if(e instanceof Chef)
        chefs.add(e);
    else if(e instanceof Manager)
        managers.add(e);
    else
        others.add(e);
}

Collections.sort(managers);

List<Employee> result = new ArrayList<Employee>();
result.addAll(chefs);
result.addAll(managers);
result.addAll(others);

ArrayList::addAll使用System::arrayCopy因此速度相当快。这与使用比较器大致相同,但可能更具可读性。这真的取决于偏好。

答案 2 :(得分:1)

你的代码没有编译,你不能在getSalary返回的原始int上调用compareTo()而是可以使用实用程序方法Long.compare()

if (this instanceof Manager || employee2 instanceof Manager) {
    return Long.compare(this.getSalary(), employee2.getSalary());
}

另外,鉴于这不是一个“自然搜索顺序”,我将其表示为比较器而不是Comparable ......,即Employee类的外部,而不是嵌入其中。

Collections.sort(employees, new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        if (o1 instanceof Manager && o2 instanceof Chef) {
            return 1;
        } else if (o1 instanceof Chef && o2 instanceof Manager) {
            return -1;
        } else if (o1 instanceof Chef && o2 instanceof Chef) {
            return 0;
        } else {
            return Long.compare(o1.getSalary(), o2.getSalary());
        }
    }
});

输出

Chef 25000
Chef 28000
Chef 29000
Chef 25500
Manager 40000
Manager 50000
Manager 60000

答案 3 :(得分:1)

您可以创建自定义比较器并将其传递给Collections.sort(..)。有比较器后,您可以执行Collections.sort(employees, new CustomComparator());

以下是这样做的一种方法,但我并不强烈建议这种做法,因为它会在运行时引起复杂化,甚至在检查/反对子类时会出错。

public class CustomComparator implements Comparator<Employee> {
    @Override
    public int compare(Employee o1, Employee o2) {
        if(o1 instanceof Chef && o2 instanceof Manager)
            return -1;

        if(o1 instanceof Manager && o2 instanceof Chef)
            return +1;

        if(o1 instanceof Manager && o2 instanceof Manager) 
            return Double.compare(o1.getSalary(), o2.getSalary());

        return 0;
    }  
}

在我写这篇文章时,我认为更好的方法是使用多个列表来存储每个Employee类型。这将解决在运行时使用检查的问题。

或者,也许您可​​以在界面中实现一些字段,为每种实现类型提供不同的分数,这些分类又可用于比较Employee个对象。