TreeSet违反了Set契约?

时间:2013-07-12 07:12:31

标签: java collections treeset

我试图在论坛中回答this问题,但我发现尽管覆盖了equals类中的Employee方法,但我仍然能够将重复的元素添加到TreeSet

TreeSet.add(E)方法的Javadoc说

  

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

这实质上意味着没有2个等于对象将被插入TreeSet,并且相等仅由所包含对象的equals()方法确定。

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

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允许多个元素,即使它们相等?

现在我改变了compareTo这样的Employee方法

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

输出

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

覆盖TreeSetcompareTo如何正常工作?

3 个答案:

答案 0 :(得分:9)

TreeSet的javadoc也说:

  

请注意,如果要正确实现Set接口,则由集合维护的排序(无论是否提供显式比较器)必须与equals 一致。 (有关与equals 一致的精确定义,请参阅ComparableComparator。)这是因为Set接口是根据{{1操作,但是equals实例使用其TreeSet(或compareTo)方法执行所有元素比较,因此从这个方法看,两个被认为相等的元素是设定,平等。集合的行为定义良好,即使它的排序与equals不一致;它只是不遵守compare接口的一般合同。

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

答案 1 :(得分:0)

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

据我所知,没有对equals进行明确的测试,这意味着如果你的对象不是明确相同的对象(即'=='),你的对象就会被添加。

我认为这些文档具有误导性,但我猜这是说它是正式的语言表达。这是不明确的,所以我可能是错的,但我很确定凭借底层地图的密钥保证了唯一性。仅供参考:以对象为键传递虚拟值。

答案 2 :(得分:0)

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

因为在你的第一个compareTo方法版本中,你使用employeeId进行比较,并为对象e1和e2添加了不同的emoplyee id。

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

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