我有以下代码,根据他们的经验对Employees's
进行排序。
我正在添加2名员工,其中name
和experience
不同。我期待最后set
将有2名员工,但我只有一名员工。
我还覆盖了equals
和hashcode
,任何人都可以告诉我为什么我只有一名员工。
测试类
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.junit.Test;
public class SetWithComparator {
@Test
public void testComparatorWithSet() {
Comparator<Employee> comparator =
(emp1, emp2) -> emp1.getYearOFExp().compareTo(emp2.getYearOFExp());
Set<Employee> empSet = new TreeSet<>(comparator);
Employee e1 = new Employee();
e1.setName("Employee-1");
e1.setYearOFExp(12f);
Employee e2 = new Employee();
e2.setName("Employee-2");
e2.setYearOFExp(12f);
empSet.add(e1);
empSet.add(e2);
}
}
模型类
class Employee {
private String name;
private Float yearOFExp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getYearOFExp() {
return yearOFExp;
}
public void setYearOFExp(Float yearOFExp) {
this.yearOFExp = yearOFExp;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Employee) {
Employee e = (Employee) obj;
return new EqualsBuilder().append(name, e.getName()).isEquals();
} else {
return false;
}
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(name).toHashCode();
}
}
答案 0 :(得分:8)
因为比较器与你的equals方法不一致。请查看Comparator的文档。
比较器c对一组元素S施加的排序被称为 当且仅当c.compare(e1,e2)== 0具有时,才与equals一致 对于S中的每个e1和e2,与e1.equals(e2)相同的布尔值。
使用能够比较的比较器时应该小心 强加与equals不一致的排序以排序有序集 (或排序的地图)。假设一个带有显式的有序集(或有序映射) 比较器c与从集合S中绘制的元素(或键)一起使用 c对S施加的排序与equals,排序后的顺序不一致 set(或有序地图)将表现得“奇怪”。特别是排序 set(或有序映射)将违反set的一般合同(或 map),用等号定义。
您在Comparable的文档中暗示了您遇到的确切行为(尽管您使用的是比较器):
例如,如果添加两个键a和b使得(!a.equals(b)&amp;&amp; a.compareTo(b)== 0)到不使用显式的有序集 比较器,第二个add操作返回false(和大小 排序集不会增加)因为a和b是等价的 排序集的视角。
在您的情况下:comparator.compare(e1, e2)
为0
,e1.equals(e2)
为false
。
答案 1 :(得分:4)
对于SortedSet
,Comparator
确定哪些元素相同且不包含重复项。如果您不想将具有相同经验的所有员工视为相同,则必须添加二级订单:
Comparator<Employee> comparator = Comparator.comparing(Employee::getYearOFExp)
.thenComparing(Employee::getName);
请注意,您必须包含构成员工身份的所有属性。在您的示例中,只有名称,但在现实生活中,您可以拥有更多。另一方面,如果你有一个确定身份的ID,你不需要检查其他属性,事实上,不应该,因为大多数属性,包括名称,都可以更改。这也适用于equals
和hashCode
。
为了警告可以假设给予什么的错误假设,性别变化是一个真实的生活事实,甚至生日可能会变得虚假,需要纠正。