当我试图找出如何正确使用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());
那是为什么?
答案 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
并不依赖Comparator
或Comparable
接口来确定顺序或唯一性;相反,它依赖于放置在其中的各个元素的hashCode
。
这就是为什么您不能将其添加为构造函数参数的原因;尝试插入具有自然顺序的 hash 映射值没有任何意义。除非集合的实现另有规定,否则集合默认为无序; HashSet
是集合的无序实现。
PriorityQueue
和TreeMap
与比较器和Comparable
元素一起工作的原因是由于它们的结构方式。 PriorityQueue
和TreeMap
都对元素in natural order进行排序,因此最好在没有元素实现Comparator
的情况下使用Comparable
驱动。这些数据结构是 ordered ,因此强加自定义排序是这些功能的特征。