【问题标题】:TreeSet violating Set contract?TreeSet 违反 Set 合同?
【发布时间】:2013-07-12 07:12:30
【问题描述】:

我试图在论坛中回答this 的问题,我发现尽管在Employee 类中覆盖了equals 方法,我仍然可以向TreeSet 添加重复的元素。

TreeSet.add(E)方法的Javadoc说

如果指定元素尚不存在,则将其添加到此集合中。 更正式地说,如果集合,则将指定元素 e 添加到此集合中 不包含元素 e2 使得 (e==null ? e2==null : e.equals(e2))。 如果该集合已经包含该元素,则调用离开该集合 不变并返回 false。

这实质上意味着不会将 2 个等于对象插入到 TreeSet 中,并且相等性仅由包含对象的 equals() 方法确定。

但是,下面的代码将 2 个元素添加到 Set,即使它们是相等的

public class Employee implements Comparable<Employee> {

    String employeeName;
    int employeeId;

    public Employee(String name, int id) {
        this.employeeName = name;
        this.employeeId = id;
    }

    public int compareTo(Employee emp) {
        //return this.employeeName.compareTo(emp.employeeName);
        return (this.employeeId - emp.employeeId);
    }

    @Override
    public String toString() {
        return ("Name is: " + employeeName + " Emp id is: " + employeeId);
    }

    @Override
    public boolean equals(Object emp) {
        if(emp instanceof Employee &&((Employee)emp).employeeName.equals(this.employeeName)){
            return true;
        }
        return false;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Set<Employee> set = new TreeSet<Employee>();
        Employee e1 = new Employee("A", 1);
        Employee e2 = new Employee("A", 2);
        System.out.println(e1.equals(e2));
        set.add(e1);
        set.add(e2);
        System.out.println(set);
    }

}

这是输出

true
[Name is: A Emp id is: 1, Name is: A Emp id is: 2]

为什么TreeSet 允许多个元素,即使它们相等?

现在我像这样更改了EmployeecompareTo 方法

public int compareTo(Employee emp) {
    return this.employeeName.compareTo(emp.employeeName);
}

输出是

true
[Name is: A Emp id is: 1]

TreeSet 在覆盖 compareTo 后如何正常工作?

【问题讨论】:

    标签: java collections treeset


    【解决方案1】:

    TreeSet 的 javadoc 也说:

    请注意,如果要正确实现Set 接口,集合维护的顺序(无论是否提供显式比较器)必须与equals一致。 (参见ComparableComparator 了解与equals 一致 的精确定义。)这是因为Set 接口是根据equals 操作定义的,但@987654326 @instance 使用其compareTo(或compare)方法执行所有元素比较,因此从集合的角度来看,此方法认为相等的两个元素是相等的。集合的行为是明确定义的,即使它的顺序与equals不一致;它只是不遵守Set 接口的一般合同。

    我同意 add() 的 javadoc 可以更清楚地表明 equals() 并未实际使用,但重新声明或链接到此警告。

    【讨论】:

    • @sanbhat 这也在SortedSet 中指定,TreeSet 实现SortedSet
    • 感谢@LuiggiMendoza 我最初找不到它...我正在寻找由一组维护的排序(无论是否提供显式比较器)必须与等于一致
    【解决方案2】:

    Set 实现在后台使用Map 来管理数据,并通过Map 的键来保证对象的唯一性。

    据我所知,没有对 equals 进行明确测试,这意味着如果您的对象不是明确相同的对象(即“==”),则会添加它。

    我认为这些文档具有误导性,但我猜它是在说它是正式的语言表示。这是模棱两可的,所以我可能是错的,但我很确定唯一性是通过底层地图的键来保证的。仅供参考:以您的对象为键传递一个虚拟值。

    【讨论】:

    • 这与Set 的实现无关。注意TreeSet 实现NavigableSetSortedSet(参考here)和SortedSet 状态使用natural ordering(参考here)。
    【解决方案3】:

    关于 TreeSet 的第一件事是,它还保持对象的顺序,同时对它们进行排序并使用 compareTo 方法。

    因为在第一个版本的 compareTo 方法中,您使用 employeeId 进行比较,并为对象 e1 和 e2 放置不同的 emplyee id。

    Treeset 会将其视为不同的对象,因为比较不会发送零值(它将发送 + 或 - 值)。

    但在第二个版本中,您使用 employeeName 实现 compareTo,这对于对象 e1 和 e2 是相同的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      相关资源
      最近更新 更多