如果我的类在Java中实现可比性,我是否需要equals和Hashcode方法?

时间:2013-08-22 23:40:48

标签: java equals hashcode comparable

我在can StringBuffer objects be keys in TreeSet in Java?

上发现了这条评论

“Java中的地图使用了2种识别策略(或多或少)。

散列:输入“Foo”被转换为尽可能最好的尝试,以生成唯一访问数组索引的数字。 (纯粹主义者,请不要虐待我,我故意简化)。此索引是存储值的位置。 “Foo”和“Bar”实际上可能生成相同的索引值,这意味着它们都将映射到相同的数组位置。显然这不起作用,所以这就是“equals()”方法的用武之地;它用于消除歧义

比较:通过使用比较方法,您不需要这个额外的消歧步骤,因为比较首先不会产生这种碰撞。 “Foo”等于的唯一关键是“Foo”。一个非常好的想法是,如果你可以将“equals()”定义为compareTo()== 0;为了一致的缘故。不是要求。“

我的问题如下: 如果我的类实现了可比性,那么它是否意味着我不必重写equals和hashcode方法来将我的对象用作Hash集合中的键。例如

class Person implements Comparable<Person> {
     int id;
     String name;

     public Person(int id, String name) {
        this.id=id;
        this.name=name;
     }

    public int compareTo(Person other) {
      return this.id-other.id;
    }
}

现在,我可以在Hashable集合中使用Person对象吗?

5 个答案:

答案 0 :(得分:6)

你正在谈论的文章TreeSet。树集是一棵树,每个节点都有一个由它的值定义的地方,与树中已有的其他值相比较。

hashTable在哈希表中存储键/值对。使用Hashtable时,您可以指定用作键的对象,以及要链接到该键的值。然后对密钥进行哈希处理,并将生成的哈希代码用作值存储在表中的索引。

HashableTreeSet之间的区别在于树集不需要hashCode,只需要知道您是否需要在树中左右移动项目。为此你可以使用比较而已。

在hashTable中,比较就足够了,因为它的构建方式不同,每个对象都通过对其进行散列来到达他的单元格,而不是通过将它与集合中已有的项目进行比较。

所以答案是否定的,你可以'Person在哈希表中使用compareTo。你必须覆盖hashCode()equals()

我还建议您阅读关于哈希表的this文章

答案 1 :(得分:2)

HashTable确实使用了equals和hashCode。每个班级都有这些方法。如果您不实现它们,则继承它们。

是否需要实现它们取决于继承的版本是否适合您的目的。特别是,由于Person没有指定的超类,因此它继承了Object方法。这意味着Person对象只与自身相等。

您是否需要将两个不同的Person对象视为与HashTable键相等?

答案 2 :(得分:1)

  

如果我的类实现了可比性,那么它是否意味着我不必重写equals和hashcode方法来将我的对象用作Hash集合中的键。例如

不,您仍然需要实施equals()hashCode()。这些方法执行非常不同的功能,不能被compareTo()替换。

  • equals()根据对象的相等性返回一个布尔值。这通常是身份相等而不是字段相等。这可能与用于比较compareTo(...)中的对象的字段有很大不同,尽管如果它对实体有意义,equals()方法可以是:

    @Overrides
    public boolean equals(Object obj) {
         if (obj == null || obj.getClass() != getClass()) {
            return false;
         } else {
             return compareTo((Person)obj) == 0;
         }
    }
    
  • hashCode()返回实例的整数值,该实例在哈希表中用于计算应放入的存储桶。没有等效方法可以从compareTo(...)中获取此值

答案 3 :(得分:0)

TreeSet需要Comparable,在树的右侧或左侧添加值。 HashMap需要可从Object Class获得的equals()和Hashcode()方法,但您必须为了您的目的而覆盖它们。

答案 4 :(得分:0)

如果一个类实现Comparable,那表明该类的实例代表某种类型的值;通常,当类封装值时,可能存在两个不同的实例,它们保持相同的值,因此应该被认为是等价的。由于将不同对象实例视为等效的唯一方法是覆盖equalshashCode,这意味着实现Comparable的内容应覆盖equals和{ {1}} 除非 hashCode操作的封装值将全局唯一(暗示不应将相同的实例视为等效)。

举个简单的例子,假设一个类包含compare类型的CreationRank字段;每次创建实例时,该成员都设置为从单个long获取的值,AtomicLong使用该字段按创建顺序对对象进行排序。没有两个不同的类实例会报告相同的Comparable;因此,CreationRankx.equals(y)引用相同的对象实例的唯一方法x应该是真的 - 完全是默认y和{{1}的方式工作。

BTW,equals返回0通常意味着hashCode将返回true,反之亦然,但在某些情况下x.compare(y)可能为false但x.equals(y)尽管如此,应该归零。当一个对象封装一些可以排序的属性而另一些不能排序时,可能就是这种情况。例如,考虑一个封装x.equals(y)的低精度x.compare(y)类型和FutureAction接口的实现。这些事情可以根据封装的日期和时间进行排名,但可能没有明智的方法来排列具有相同日期和时间但行动不同的两个项目。如果DateTime报告为零,则DoSomething报告为false会比假装明确不相等的项目称为“相等”更有意义。