试图通过HashSet的构造函数传递比较器

时间:2019-02-22 20:35:27

标签: java lambda comparator

当我试图找出如何正确使用Comparator时,我正在尝试将Employee排序为HashSet。所以我这样做了:

Set<Employee> EmployeeSet = new HashSet<Employee>((a,b)->a.getAge()-b.getAge());

如您所见,我尝试按年龄对其进行排序,但是当我使用此lambda表达式时,它会产生编译错误,因此我想这里不对。

这是我的Employee班:

class Employee {    
    String name;
    int age;
    // constructor, getters and setters
}

修改

有了PriorityQueue,它可以完美工作:

Queue<Employee> list = new PriorityQueue<Employee>((a,b)->a.getAge()-b.getAge());

那是为什么?

3 个答案:

答案 0 :(得分:5)

您可以使用TreeSet来确保基于Set的{​​{1}}有序

Comparator

另一方面,HashSet在其构造函数中不接受Set<Employee> employeeSet = new TreeSet<>(Comparator.comparingInt(Employee::getAge)); // (a, b) -> a.getAge() - b.getAge() >>> Comparator.comparingInt(Employee::getAge 进行初始化。

编辑

Comparator

工作正常,因为PriorityQueue还是一个有序集合,在其构造函数之一中接受Queue<Employee> list = new PriorityQueue<>(Comparator.comparingInt(Employee::getAge));

答案 1 :(得分:2)

正如其他人所指出的,HashSet是无序集合,因此它不能采用提供排序顺序的比较器。您可能想使用诸如TreeSet之类的排序集。

    Set<Employee> employeeSet = new TreeSet<>((a,b)->a.getAge()-b.getAge());

有关数字比较的快速注释。使用减法比较int值是危险的,因为如果减法溢出,它将给出错误的结果。对于通常年龄范围不会导致溢出的年龄段的人,这不会发生。但是,如果有人要复制此代码并将其应用于不同的值,则可能会导致错误。相反,我建议使用Comparator实用程序方法来构造正确的比较函数:

    Set<Employee> employeeSet = new TreeSet<>(Comparator.comparingInt(Employee::getAge));

这确实将按年龄对员工进行排序。但是...

    Employee terry = new Employee("Terry", 37);
    Employee kelly = new Employee("Kelly", 26);
    Employee brett = new Employee("Brett", 32);
    Employee chris = new Employee("Chris", 26);
    List<Employee> employeeList = Arrays.asList(terry, kelly, brett, chris);
    Set<Employee> employeeSet = new TreeSet<>(Comparator.comparingInt(Employee::getAge));
    employeeSet.addAll(employeeList);
    System.out.println(employeeSet);

    [Employee{name=Kelly, age=26}, Employee{name=Brett, age=32}, Employee{name=Terry, age=37}]

我们增加了四名员工,但集合中只有三名员工!另外:

    System.out.println(employeeSet.contains(kelly)); // true
    System.out.println(employeeSet.contains(chris)); // true

这怎么可能?提供的比较器不仅用于排序,而且还用于确定集合成员资格。由于凯利(Kelly)和克里斯(Chris)的年龄相同,因此比较者认为它们相等,因此只有一个最终进入集合。

您可能不想使用TreeSet来对可能具有重复项的值进行排序,因为这将导致项目丢失。您可以做几件事。一,您可以将员工放入列表,然后对列表进行排序:

    employeeList.sort(Comparator.comparingInt(Employee::getAge));

    // before
    [Employee{name=Terry, age=37}, Employee{name=Kelly, age=26}, Employee{name=Brett, age=32}, Employee{name=Chris, age=26}]

    // after
    [Employee{name=Kelly, age=26}, Employee{name=Chris, age=26}, Employee{name=Brett, age=32}, Employee{name=Terry, age=37}]

或者,您可以编写一个更复杂的比较器来区分输入数据的每个元素,这样就不会再有两个被认为是重复的。在一个实际的示例中,员工可以使用相同的名称,因此我们可能还希望包括对真正唯一的内容(例如员工ID)进行比较。但是,对于这个简单的示例,我们可以对员工的姓名进行排序:

    Set<Employee> employeeSet = new TreeSet<>(Comparator.comparingInt(Employee::getAge)
                                                        .thenComparing(Employee::getName));

添加所有员工后,结果为:

    [Employee{name=Chris, age=26}, Employee{name=Kelly, age=26}, Employee{name=Brett, age=32}, Employee{name=Terry, age=37}]

答案 2 :(得分:1)

从机械上讲,HashSet并不依赖ComparatorComparable接口来确定顺序或唯一性;相反,它依赖于放置在其中的各个元素的hashCode

这就是为什么您不能将其添加为构造函数参数的原因;尝试插入具有自然顺序的 hash 映射值没有任何意义。除非集合的实现另有规定,否则集合默认为无序HashSet是集合的无序实现。

PriorityQueueTreeMap与比较器和Comparable元素一起工作的原因是由于它们的结构方式。 PriorityQueueTreeMap都对元素in natural order进行排序,因此最好在没有元素实现Comparator的情况下使用Comparable驱动。这些数据结构是 ordered ,因此强加自定义排序是这些功能的特征。