为什么Set.contains()似乎没有使用o.equals()?

时间:2012-07-30 13:14:29

标签: java set equals comparable

我有一个包含包装器的TreeSet,它将Foo对象存储在某个position,定义如下:

class Wrapper implements Comparable<Wrapper> {
  private final Foo foo;
  private final Double position;

  ...

  @Override boolean equals(Object o) {

    ... 

    if(o instanceof Wrapper)
        return o.getFoo().equals(this.foo);

    if(o instanceof Foo)
        return o.equals(this.foo);
  }

  @Override public int compareTo(MarkerWithPosition o) {
      return position.compareTo(o.getPosition());
  }
}

NavigableSet<Wrapper> fooWrappers = new TreeSet<Wrapper>();

因为我希望我的TreeSetposition排序,但可以foo进行搜索。但是当我执行这些操作时:

Foo foo = new Foo(bar);
Wrapper fooWrapper = new Wrapper(foo, 1.0);
fooWrappers.add(fooWrapper);

fooWrapper.equals(new Wrapper(new Foo(bar), 1.0));
fooWrapper.equals(new Foo(bar));
fooWrappers.contains(fooWrapper);
fooWrappers.contains(new Wrapper(foo, 1.0));
fooWrappers.contains(new Wrapper(new Foo(bar), 1.0));
fooWrappers.contains(new Wrapper(foo, 2.0));
fooWrappers.contains(foo);

我明白了:

true
true
true
true
true
false
Exception in thread "main" java.lang.ClassCastException: org.gridqtl.Marker cannot be cast to java.lang.Comparable
    at java.util.TreeMap.getEntry(TreeMap.java:325)
    at java.util.TreeMap.containsKey(TreeMap.java:209)
    at java.util.TreeSet.contains(TreeSet.java:217)

当我希望他们都返回true时,似乎TreeSet.contains没有使用我的equals方法作为API suggests。我需要覆盖另一种方法吗?

2 个答案:

答案 0 :(得分:9)

TreeSet是一个确实使用compareTo的Set实现,正如javadoc中强调的那样:

  

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

答案 1 :(得分:0)

TreeSet是一个有序集。

equals无法为您提供订购信息,因此TreeSet必须使用其他内容。

此“其他内容”是Comparable界面或其堂兄Comparator界面。

两个接口都提供有关如何订购类的2个对象的信息。