Java Bean的equal、hashCode方法

 

 

通常写javaBean,除了属性及其gettersetter方法,还有equals,hashCodetoString方法。

 

默认的hashCode(),返回int值。我们可以重写hashCode,但每个objecthashCode值应该是不一样的。

 

默认的equals(),返回boolean值。如果不是一个object,会返回false。我们可以重写它,比如:hashCode不同则返回false。又如:对象的id值不同就返回false。具体根据需求来决定。

 

默认的toString(),返回String值,即对象的类类型及其内存中的地址组成的字符串。我们可以重写它,让它更具体的显示对象的详细信息。比如,当写日志的时候就可以直接方便的调用toString()了。

 

 

Object类是所有Java类的根基类(Java.lang.object)

如果在类的声明中未使用extend关键字指明其基类,则默认基类为Object

如:public class Person{...}等价于

      public class Person extends Object{...}

 

碰上整型与字符串变量运算,首先把整型变量转化成字符串变量再运算。

    

public class TesToString {

  public static void main(String[] args) {

      Dog d = new Dog();

      System.out.println("d:="+d);

  }

}

 

//重写toString方法,如果不重写,查询API文档可知返回的是 类名@16进制哈希码

class Dog{

  public String toString() {

      return "A Dog!";

  }

}

 

hashcode解释:

    它独一无二的代表一个对象,并且通过hashcode可以找到对象的位置。在内存中有好多好多对象,在程序运行时有很多对象可能在内存中分配。但对虚拟机来说,它运行时它要找到对象的地址,于是用表,通常是哈希表来存储内存中对象的编码,每个对象对应独一无二的编码。由编码就能找到相应的对象,由此快速对应对象。

 

equals方法(比较的是内容,判断是否逻辑相等)

“==” (比较的是地址,只有两个都是指向同一个对象的时候才能返回True

例:

 

下面让我们来看一下摘录的一段对话,比较通俗:

 

ZJ 21:00:06

对了 老师,为什么在HIBERNA里要重写HASCODE EQUALS这两个方法?

付老实 21:04:22

equals用来按照自己的规则判断两个对象是否相等,而重写了equals后,按照java的惯例,就需要重写hashCode

ZJ 21:05:11

老师 只看懂的一点点呀,再稍微说多点啊!

付老实 21:08:04

这么说罢

付老实 21:08:23

1,重点是equals,重写hashCode只是技术要求(为了提高效率)

付老实 21:09:02

2,为什么要重写equals呢,因为在java的集合框架中,是通过equals来判断两个对象是否相等的

付老实 21:10:03

3,在hibernate中,经常使用set集合来保存相关对象,而set集合是不允许重复的,但是下面的程序,你判断一下运行结果

付老实 21:10:44

Set user = new HashSet();

user.add(new Book("精通struts"));

user.add(new Book("精通struts"));

System.out.println(user.size());

Neo 21:10:48

这两个为什么相同阿???

就是说set集合是按照equals来判断是否重复的?

付老实 21:12:00

恩,猜一下打印的结果

ZJ 21:11:31

应该输出1吧,或许报错 呵呵

付老实 21:12:44

错了

付老实 21:12:58

完全取决于Book类有没有重写equals方法

付老实 21:13:39

如果没有重写,默认equals是比较地址,那么这两个book对象不一样,输出2,意味着hibernate会认为这是两个对象,再接下来的持久化过程中可能会出错

付老实 21:14:07

如果重写了equals,比如按照主键(书名)比较,那么这两个对象是一样的,输出1

付老实 21:14:10

明白了?

ZJ 21:13:26

明白了

付老实 21:14:32

再说hashCode

ZJ 21:13:51

付老实 21:14:57

equals方法虽好,但是效率相对底下   

ZJ 21:14:43

老实接着说说   

付老实 21:15:45

典型的equals实现

public boolean equals(Object obj) {

     Book b = (Book)obj;

     return this.name.equals(b.name);

}

付老实 21:16:43

其间需要向下转型,调用其他类的equals等操作,有可能比较费时,特别是比较规则比较复杂的时候

ZJ 21:16:33

能理解

付老实 21:18:04

hashCode为每一个对象生成一个散列码(通过一种神秘的算法,一般为关键属性乘以一个质数),避免了比较慢的运算

付老实 21:18:27

但是hashCode并不能保证能为每一个不同的对象生成唯一的散列码

ZJ 21:18:34

那若是散列码重复了呢?

付老实 21:19:59

所以在java的集合中,判断两个对象是否相等的规则是:

1,判断两个对象的hashCode是否相等

     如果不相等,认为两个对象也不相等,完毕

     如果相等,转入2

2,判断两个对象用equals运算是否相等

     如果不相等,认为两个对象也不相等

     如果相等,认为两个对象相等

完毕

 

Neo 21:19:52

String好像是一个特例阿。。。

付老实 21:20:59

由此可以看出,一个好的散列码算法可以加快程序的速度,apache专门有个工具可以为类生成hashCodeMyEclipse 5.0中也有一个工具

付老实 21:21:09

String没有什么特殊的,也是这个原则

Neo 21:21:39

"123""123"这两个字符串为什么相等?

付老实 21:23:00

晕,你说呢

Neo 21:22:07

。。。

Neo 21:22:10

想起来了

Neo 21:22:19

池是把?

付老实 21:23:20

Neo 21:22:36

云,忘了~

付老实 21:23:49

好了,下去再去看看《深入浅出Hibernate》的210页吧

 

 

    每个Java对象都有hashCode() equals()方法。许多类忽略(Override)这些方法的缺省实施,以在对象实例之间提供更深层次的语义可比性。在Java理念和实践这一部分,Java开发人员Brian Goetz向您介绍在创建Java类以有效和准确定义hashCode()equals()时应遵循的规则和指南。您可以在讨论论坛与作者和其它读者一同探讨您对本文的看法。(您还可以点击本文顶部或底部的讨论进入论坛。)

 

   虽然Java语言不直接支持关联数组 -- 可以使用任何对象作为一个索引的数组 -- 但在根Object类中使用hashCode()方法明确表示期望广泛使用HashMap(及其前辈Hashtable)。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式中支持散列可以促进基于散列的容器的开发和使用。

 

定义对象的相等性

 

   Object类有两种方法来推断对象的标识:equals()hashCode()。一般来说,如果您忽略了其中一种,您必须同时忽略这两种,因为两者之间有必须维持的至关重要的关系。特殊情况是根据equals() 方法,如果两个对象是相等的,它们必须有相同的hashCode()(尽管这通常不是真的)

 

  特定类的equals()的语义在Implementer的左侧定义;定义对特定类来说equals()意味着什么是其设计工作的一部分。Object提供的缺省实施简单引用下面等式:

 

   public boolean equals(Object obj) { return (this == obj); }

 

  在这种缺省实施情况下,只有它们引用真正同一个对象时这两个引用才是相等的。同样,Object提供的hashCode()的缺省实施通过将对象的内存地址对映于一个整数值来生成。由于在某些架构上,地址空间大于int值的范围,两个不同的对象有相同的hashCode()是可能的。如果您忽略了 hashCode(),您仍旧可以使用System.identityHashCode()方法来接入这类缺省值。

 

忽略 equals() -- 简单实例

 

  缺省情况下,equals()hashCode()基于标识的实施是合理的,但对于某些类来说,它们希望放宽等式的定义。例如,Integer类定义equals() 与下面类似:

 

   public boolean equals(Object obj) {

   return (obj instanceof Integer

   && intValue() == ((Integer) obj).intValue());

   }

 

  在这个定义中,只有在包含相同的整数值的情况下这两个Integer对象是相等的。结合将不可修改的Integer,这使得使用Integer作为 HashMap中的关键字是切实可行的。这种基于值的Equal方法可以由Java类库中的所有原始封装类使用,如IntegerFloat CharacterBoolean以及String(如果两个String对象包含相同顺序的字符,那它们是相等的)。由于这些类都是不可修改的并且可以实施hashCode()equals(),它们都可以做为很好的散列关键字。

 

为什么忽略 equals()hashCode()?

 

   如果Integer不忽略equals() hashCode()情况又将如何?如果我们从未在HashMap或其它基于散列的集合中使用Integer作为关键字的话,什么也不会发生。但是,如果我们在HashMap中使用这类Integer对象作为关键字,我们将不能够可靠地检索相关的值,除非我们在get()调用中使用与put()调用中极其类似的Integer实例。这要求确保在我们的整个程序中,只能使用对应于特定整数值的Integer对象的一个实例。不用说,这种方法极不方便而且错误频频。

 

   Objectinterface contract要求如果根据 equals()两个对象是相等的,那么它们必须有相同的hashCode()值。当其识别能力整个包含在equals()中时,为什么我们的根对象类需要hashCode()hashCode()方法纯粹用于提高效率。Java平台设计人员预计到了典型Java应用程序中基于散列的集合类(Collection Class)的重要性--HashtableHashMapHashSet,并且使用equals()与许多对象进行比较在计算方面非常昂贵。使所有Java对象都能够支持 hashCode()并结合使用基于散列的集合,可以实现有效的存储和检索。

 

实施equals()hashCode()的需求

 

   实施equals() hashCode()有一些限制,Object文件中列举出了这些限制。特别是equals()方法必须显示以下属性:

 

   Symmetry:两个引用,a b,a.equals(b) if and only if b.equals(a)

   Reflexivity:所有非空引用, a.equals(a)

   TransitivityIf a.equals(b) and b.equals(c), then a.equals(c)

   Consistency with hashCode():两个相等的对象必须有相同的hashCode()

 

   Object的规范中并没有明确要求equals() hashCode() 必须一致 -- 它们的结果在随后的调用中将是相同的,假设“不改变对象相等性比较中使用的任何信息。”这听起来象“计算的结果将不改变,除非实际情况如此。”这一模糊声明通常解释为相等性和散列值计算应是对象的可确定性功能,而不是其它。

 

对象相等性意味着什么?

 

   人们很容易满足Object类规范对equals() hashCode() 的要求。决定是否和如何忽略equals()除了判断以外,还要求其它。在简单的不可修值类中,如Integer(事实上是几乎所有不可修改的类),选择相当明显 -- 相等性应基于基本对象状态的相等性。在Integer情况下,对象的唯一状态是基本的整数值。

 

   对于可修改对象来说,答案并不总是如此清楚。equals() hashCode() 是否应基于对象的标识(象缺省实施)或对象的状态(IntegerString)?没有简单的答案 -- 它取决于类的计划使用。对于象ListMap这样的容器来说,人们对此争论不已。Java类库中的大多数类,包括容器类,错误出现在根据对象状态来提供 equals()hashCode()实施。

 

  如果对象的hashCode()值 可以基于其状态进行更改,那么当使用这类对象作为基于散列的集合中的关键字时我们必须注意,确保当它们用于作为散列关键字时,我们并不允许更改它们的状 态。所有基于散列的集合假设,当对象的散列值用于作为集合中的关键字时它不会改变。如果当关键字在集合中时它的散列代码被更改,那么将产生一些不可预测和 容易混淆的结果。实践过程中这通常不是问题 -- 我们并不经常使用象List这样的可修改对象做为HashMap中的关键字。

 

  一个简单的可修改类的例子是Point,它根据状态来定义equals()hashCode()。如果两个Point 对象引用相同的(x, y)座标,Point的散列值来源于xy座标值的IEEE 754-bit表示,那么它们是相等的。

 

  对于比较复杂的类来说,equals()hashCode()的行为可能甚至受到superclassinterface的影响。例如,List 接口要求如果并且只有另一个对象是List,而且它们有相同顺序的相同的Elements(Element上的Object.equals() 定义)List对象等于另一个对象。hashCode()的需求更特殊--listhashCode()值必须符合以下计算:

 

   hashCode = 1;

   Iterator i = list.iterator();

   while (i.hasNext()) {

   Object obj = i.next();

   hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());

   }

 

  不仅仅散列值取决于list的内容,而且还规定了结合各个Element的散列值的特殊算法。(String类规定类似的算法用于计算String的散列值。)

 

编写自己的equals()hashCode()方法

 

  忽略缺省的equals()方法比较简单,但如果不违反对称(Symmetry)或传递性(Transitivity)需求,忽略已经忽略的 equals() 方法极其棘手。当忽略equals()时,您应该总是在equals()中包括一些Javadoc注释,以帮助那些希望能够正确扩展您的类的用户。

 

   作为一个简单的例子,考虑以下类:

 

   class A {

   final B someNonNullField;

   C someOtherField;

   int someNonStateField;

   }

 

  我们应如何编写该类的equals()的方法?这种方法适用于许多情况:

 

   public boolean equals(Object other) {

   // Not strictly necessary, but often a good optimization

   if (this == other)

   return true;

   if (!(other instanceof A))

   return false;

   A otherA = (A) other;

   return

   (someNonNullField.equals(otherA.someNonNullField))

   && ((someOtherField == null)

   ? otherA.someOtherField == null

   : someOtherField.equals(otherA.someOtherField)));

   }

 

  现在我们定义了equals(),我们必须以统一的方法来定义hashCode()。一种统一但并不总是有效的定义hashCode()的方法如下:

 

   public int hashCode() { return 0; }

 

  这种方法将生成大量的条目并显著降低HashMaps的性能,但它符合规范。一个更合理的hashCode()实施应该是这样:

 

   public int hashCode() {

   int hash = 1;

   hash = hash * 31 + someNonNullField.hashCode();

   hash = hash * 31

   + (someOtherField == null ? 0 : someOtherField.hashCode());

   return hash;

   }

 

  注意:这两种实施都降低了类状态字段的equals()hashCode()方法一定比例的计算能力。根据您使用的类,您可能希望降低 superclassequals()hashCode()功能一部分计算能力。对于原始字段来说,在相关的封装类中有helper功能,可以帮助创建散列值,如Float.floatToIntBits

 

  编写一个完美的equals()方法是不现实的。通常,当扩展一个自身忽略了equals()instantiable类时,忽略equals() 是不切实际的,而且编写将被忽略的equals()方法(如在抽象类中)不同于为具体类编写equals()方法。关于实例以及说明的更详细信息请参阅 Effective Java Programming Language Guide, Item 7 (参考资料)

 

有待改进?

 

   将散列法构建到Java类库的根对象类中是一种非常明智的设计折衷方法 -- 它使使用基于散列的容器变得如此简单和高效。但是,人们对Java类库中的散列算法和对象相等性的方法和实施提出了许多批评。java.util中基于散列的容器非常方便和简便易用,但可能不适用于需要非常高性能的应用程序。虽然其中大部分将不会改变,但当您设计严重依赖于基于散列的容器效率的应用程序时必须考虑这些因素,它们包括:

 

  太小的散列范围。使用int而不是long作为hashCode()的返回类型增加了散列冲突的几率。

 

  糟糕的散列值分配。短strings和小型integers的散列值是它们自己的小整数,接近于其它“邻近”对象的散列值。一个循规导矩(Well-behaved)的散列函数将在该散列范围内更均匀地分配散列值。

 

  无定义的散列操作。虽然某些类,如StringList,定义了将其Element的散列值结合到一个散列值中使用的散列算法,但语言规范不定义将多个对象的散列值结合到新散列值中的任何批准的方法。我们在前面编写自己的equals()hashCode()方法中讨论的ListString或实例类A使用的诀窍都很简单,但算术上还远远不够完美。类库不提供任何散列算法的方便实施,它可以简化更先进的hashCode()实施的创建。

 

   当扩展已经忽略了equals() instantiable类时很难编写equals()。当扩展已经忽略了equals() instantiable类时,定义equals()的“显而易见的”方式都不能满足equals()方法的对称或传递性需求。这意味着当忽略 equals()时,您必须了解您正在扩展的类的结构和实施详细信息,甚至需要暴露基本类中的机密字段,它违反了面向对象的设计的原则。

 

结束语

 

通过统一定义equals()hashCode(),您可以提升类作为基于散列的集合中的关键字的使用性。有两种方法来定义对象的相等性和散列值:基于标识,它是Object提供的缺省方法;基于状态,它要求忽略equals()hashCode()。当对象的状态更改时如果对象的散列值发生变化,确信当状态作为散列关键字使用时您不允许更更改其状态。

 

以下是摘录一网站的讨论:


Hibernate Cartridge(CHB) Equals/Hashcode/ToString

 

## by schmeling Dec 04, 2008; 06:01pm :: Rate this Message: - Use ratings to moderate (?)

 

Hi,

 

in Hibernate cartridge the overridden hashcode, equals and toString methods are only generated if the entity is embedded or if you are using a natural key. In entities with automatically generated id, these operations will not be overridden which is critical in my opinion.

 

Thanks,

 

Benjamin

 

 

## by Karsten Thoms Dec 04, 2008; 07:32pm :: Rate this Message: - Use ratings to moderate (?)

 

your issues seem to be real bugs. The mailing list is the wrong place

for this, since this mails are only read once and not tracked. Be so

kind and open issues in the Bug tracker at

http://www.fornax-platform.org/tracker/secure/Dashboard.jspa

 

Kind regards,

~Karsten

 

## by Darius Jockel Dec 09, 2008; 05:15am :: Rate this Message: - Use ratings to moderate (?)

 

sorry for my late response.

 

The hashcode and equals should be derived from an unmutable subset of the attributes of an entity.

Therefore this subset has to be declared as <<NaturalKey>>.

If all attributes of an entity are the NaturalKey and you do not want to declare all of them explizit as NaturalKey, you can enable the feature 'FieldsAreNaturalKeys'.

This feature enabled, an entity without an explizit NaturalKey is handeled as all fields are NaturalKeys.

 

Enable this feature in the hibernateConfig.txt. Put an '+' in front of the FieldsAreNaturalKeys.

 

Do this solve your problem?

 

Regards

Darius

 

## by schmeling Dec 09, 2008; 05:55am :: Rate this Message: - Use ratings to moderate (?)

 

Thanks for your response.

 

    Darius Jockel wrote:

    The hashcode and equals should be derived from an unmutable subset of the attributes of an entity.

 

That's true it must be unique, not nullable and immutable.

 

    Darius Jockel wrote:

    Therefore this subset has to be declared as <<NaturalKey>>.

 

I don't think so. Also surrogate keys like the implicitly generated id attribute of entities are candidates for the implementation of hashcode and equals method. So if there is no natural key we have to override equals and hashcode using the id attribute for equals compare and calling the hashcode method. Otherwise two entities with the same identifier will not be equal nor have they the same hashcode.

 

 

## by Patrik Nordwall Dec 09, 2008; 04:29pm :: Rate this Message: - Use ratings to moderate (?)

 schmeling wrote:

    I don't think so. Also surrogate keys like the implicitly generated id attribute of entities are candidates for the implementation of hashcode and equals method. So if there is no natural key we have to override equals and hashcode using the id attribute for equals compare and calling the hashcode method. Otherwise two entities with the same identifier will not be equal nor have they the same hashcode.

 

I'm not sure that is a good idea. When an object is created, before it is persisted, it doesn't have an id. When saving the id is assigned, i.e. it is changing. Read more about how we solved it in Sculptor:

http://www.fornax-platform.org/cp/display/fornax/3.+Advanced+Tutorial+(CSC)#3.AdvancedTutorial(CSC)-Key

 

 

## by schmeling Dec 09, 2008; 06:09pm :: Rate this Message: - Use ratings to moderate (?)

 

    Patrik Nordwall wrote:

    I'm not sure that is a good idea. When an object is created, before it is persisted, it doesn't have an id. When saving the id is assigned, i.e. it is changing. Read more about how we solved it in Sculptor:

    http://www.fornax-platform.org/cp/display/fornax/3.+Advanced+Tutorial+(CSC)#3.AdvancedTutorial(CSC)-Key

 

That's true, but in the case where the id is null (before it is persisted) the equals and hashcode method should use the methods inherited from object (entities without an id are equal if they are the same object instance).  If we have not overridden the methods at all, one has to override them itself in the Impl classes, otherwise these entities with surrogate ids cannot be used with Hibernate (which would be a big limitation). My proposal is the following implementation in case of the surrogate id :

 

an example for equals with id as key:

 

public boolean equals(Object object)

{

        if (object == this)

            return true;

        else if (!(object instanceof <Entity>))

            return false;

        final <Entity> other = (<Entity>) object;

        if (this.id == null || other.getId() == null || !this.id.equals(other.getId()))

            return false;

        else

            return true;

 }

 

## by Patrik Nordwall Dec 09, 2008; 06:30pm :: Rate this Message: - Use ratings to moderate (?)

 

Reply | Reply to Author | Print | View Threaded | Show Only this Message

How do you implement hashCode?

Consider the case when the object is placed in a Map before it is persisted and then it is persisted. hashCode should not change.

 

/Patrik

 

 

 

    schmeling wrote:

 

        Patrik Nordwall wrote:

        I'm not sure that is a good idea. When an object is created, before it is persisted, it doesn't have an id. When saving the id is assigned, i.e. it is changing. Read more about how we solved it in Sculptor:

        http://www.fornax-platform.org/cp/display/fornax/3.+Advanced+Tutorial+(CSC)#3.AdvancedTutorial(CSC)-Key

 

    That's true, but in the case where the id is null (before it is persisted) the equals and hashcode method should use the methods inherited from object (entities without an id are equal if they are the same object instance).  If we have not overridden the methods at all, one has to override them itself in the Impl classes, otherwise these entities with surrogate ids cannot be used with Hibernate (which would be a big limitation). My proposal is the following implementation in case of the surrogate id :

 

    an example for equals with id as key:

 

    public boolean equals(Object object)

    {

            if (object == this)

                return true;

            else if (!(object instanceof <Entity>))

                return false;

            final <Entity> other = (<Entity>) object;

            if (this.id == null || other.getId() == null || !this.id.equals(other.getId()))

                return false;

            else

                return true;

     }

 

    ... [show rest of quote]

 

## by schmeling Dec 09, 2008; 06:52pm :: Rate this Message: - Use ratings to moderate (?)

 

Reply | Reply to Author | Print | View Threaded | Show Only this Message

You are right that's the problem, I found this also at http://www.hibernate.org/hib_docs/v3/reference/en-US/html/persistent-classes-equalshashcode.html

 

So each entity should have modeled at least one attribute that is a natural key or all attributes together should build the natural key.

 

Thanks a lot, that was a very important fact for me.

 

    Patrik Nordwall wrote:

    How do you implement hashCode?

    Consider the case when the object is placed in a Map before it is persisted and then it is persisted. hashCode should not change.

 

    /Patrik

 

## by Patrik Nordwall Dec 09, 2008; 07:31pm :: Rate this Message: - Use ratings to moderate (?)

 

You are welcome. Natural keys are always preferable, but sometimes you don't have one. Then you can add a UUID attribute, which is used for equals and hashCode. You create the UUID when the object is created and then it is never changed.

/Patrik

 

    schmeling wrote:

    You are right that's the problem, I found this also at http://www.hibernate.org/hib_docs/v3/reference/en-US/html/persistent-classes-equalshashcode.html

 

    So each entity should have modeled at least one attribute that is a natural key or all attributes together should build the natural key.

 

    Thanks a lot, that was a very important fact for me.

 

        Patrik Nordwall wrote:

        How do you implement hashCode?

        Consider the case when the object is placed in a Map before it is persisted and then it is persisted. hashCode should not change.

 

        /Patrik

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值