使用Comparator时从TreeSet丢失数据

时间:2017-01-13 06:32:20

标签: java java-8 comparator treeset

我有以下代码,根据他们的经验对Employees's进行排序。

我正在添加2名员工,其中nameexperience不同。我期待最后set将有2名员工,但我只有一名员工。

我还覆盖了equalshashcode,任何人都可以告诉我为什么我只有一名员工。

测试类

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();
    }

}

2 个答案:

答案 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)0e1.equals(e2)false

答案 1 :(得分:4)

对于SortedSetComparator确定哪些元素相同且不包含重复项。如果您不想将具有相同经验的所有员工视为相同,则必须添加二级订单:

Comparator<Employee> comparator = Comparator.comparing(Employee::getYearOFExp)
                                            .thenComparing(Employee::getName);

请注意,您必须包含构成员工身份的所有属性。在您的示例中,只有名称,但在现实生活中,您可以拥有更多。另一方面,如果你有一个确定身份的ID,你不需要检查其他属性,事实上,不应该,因为大多数属性,包括名称,都可以更改。这也适用于equalshashCode

的实施

为了警告可以假设给予什么的错误假设,性别变化是一个真实的生活事实,甚至生日可能会变得虚假,需要纠正。