java高新技术【2】 java中equals,hashcode 和 == 【经典】

如果  两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
不对,有相同的hash code。首先equals()和hashcode()这两个方法都是从object类中继承过来的。

equals()方法在object类中定义如下:

[java]  view plain copy print ?
  1. public boolean equals(Object obj) {  
  2. return (this == obj);  
  3. }  



很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。


但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。
是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。


当然了基本类型是进行值的比较,这个没有什么好说的。
我们还应该注意,Java语言对equals()的要求如下,这些要求是必须遵循的:
 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
 反射性:x.equals(x)必须返回是“true”。
 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
• 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
• 
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。


2. 其次是hashcode() 方法,在object类中定义如下:
public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。
想要明白hashCode的作用,你必须要先知道Java中的集合。总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。于是,Java采用了哈希表的原理。hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。  


3.这里我们首先要明白一个问题:
(1)equals()相等的两个对象,hashcode()一定相等;

(2)hashcode()不等,一定能推出equals()也不等;

(3)hashcode()相等,equals()可能相等,也可能不等。
equals()方法会自动调用hashCode方法么?不会.   除非你设计一个equals()方法 让它调用hashCode()方法. HashSet里的add()方法会通过一系列的调用使用到 hashCode()方法,因为它要根据hash值来确定存储位置,不是equals方法调用的.      凡是用哈希表实现的集合先比较的是hashCode方法,这是为了程序性能的问题。
当一个对象你只重写equals方法或hashCode方法,那么势必在一种情况下成立一种情况下不成立,所以只有2个方法同时改的情况下就会在各种情况下都出现你要的结果。我想这就是重写equals()方法必须重写hashCode方法的原因!
问hashcode()的函数,如果写成return 1;行不行。
我说是可以的,只是性能很差,不停的冲突。看得出来这个回答还是让他很满意的。
hashCode()返回该对象的哈希码值,该值通常是一个由该对象的内部地址转换而来的整数,它的实现主要是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的性能。
哈希函数,解决冲突。




 equals()与“==”


"==" 比较的是两个对象的引用 (references),并不是他们的内容,(只是针对于对象类型,基本类型直接使用==或!=比较内容)
在Object 中的 equals(Object) 方法其标准形式为    public boolean equals(Object obj);返回类型为 boolean ,即 true/false 与 "==" 返回类型一样。
Object 类中定义的 equals(Object) 方法是直接使用 "==" 比较的两个对象,
所以在没有覆盖 (override,或称改写、重写) equals(Object) 方法的情况下,equals(Object) 与 "==" 一样是比较的引用。


equals(Object) 方法与 "==" 相比的特殊之处就在于它可以覆盖,所以我们可以通过覆盖的办法让它不是比较引用而是比较数据内容。
当然 JDK 中也有覆盖过 equals(Object) 方法的类,如 java.lang.String,它就覆盖了从 Object 继承来的的 equals(Object) 方法,用以比较字符串内容是否相同。


看看下面这个例子:

[java]  view plain copy print ?
  1. public class Example1    {  
  2.  public static void main(String[] args){  
  3.      String s1=new String("abc");  
  4.      String s2=new String("abc");  
  5.      System.out.println("用 == 比较结果");  
  6.     System.out.println(s1==s2);//false  
  7.     System.out.println("用equals(Object) 比较结果");  
  8.     System.out.println(s1.equals(s2));//true  
  9.  }  
  10.    }  

例中用 equals(Object) 比较结果为 true。用 == 比较结果为 false。String.equals(Object) 方法直接比较了两个字符串的内容,如果相同则返回 true ,否则返回 false。
由于和对象相关的变量属于引用类型,使用obj1=obj2;obj1只是得到一份obj2应用的副本,而不是obj2对象本身。


hash code、equals和“==”三者的关系


1.如果是基本变量,没有hashcode和equals方法,基本变量的比较方式就只有==,;


2.如果是变量,由于在java中所有变量定义都是一个指向实际存储的一个句柄(你可以理解为c++中的指针),在这里==是比较句柄的地址(你可以理解为指针的存储地址),而不是句柄指向的实际内存中的内容,如果要比较实际内存中的内容,那就要用equals方法,但是!!!


如果是你自己定义的一个类,比较自定义类用equals和==是一样的,都是比较句柄地址,因为自定义的类是继承于object,而object中的equals就是用==来实现的,你可以看源码。


那为什么我们用的String等等类型equals是比较实际内容呢,是因为String等常用类已经重写了object中的equals方法,让equals来比较实际内容,你也可以看源码。


3. hashcode
在一般的应用中你不需要了解hashcode的用法,但当你用到hashmap,hashset等集合类时要注意下hashcode。


你想通过一个object的key来拿hashmap的value,hashmap的工作方法是,通过你传入的object的hashcode在内存中找地址,当找到这个地址后再通过equals方法来比较这个地址中的内容是否和你原来放进去的一样,一样就取出value。


所以这里要匹配2部分,hashcode和equals
但假如说你new一个object作为key去拿value是永远得不到结果的,因为每次new一个object,这个object的hashcode是永远不同的,所以我们要重写hashcode,你可以令你的hashcode是object中的一个恒量,这样永远可以通过你的object的hashcode来找到key的地址,然后你要重写你的equals方法,使内存中的内容也相等。。。
首先,从语法角度,也就是从强制性的角度来说,hashCode和equals是两个独立的,互不隶属,互不依赖的方法,equals成立与hashCode相等这两个命题之间,谁也不是谁的充分条件或者必要条件。 


但是,从为了让我们的程序正常运行的角度,我们应当向Effective Java中所言 


重载equals的时候,一定要(正确)重载hashCode 


使得equals成立的时候,hashCode相等,也就是a.equals(b)->a.hashCode() == b.hashCode(),或者说此时,equals是hashCode相等的充分条件,hashCode相等是equals的必要条件(从数学课上我们知道它的逆否命题:hashCode不相等也不会equals),但是它的逆命题,hashCode相等一定equals以及否命题不equals时hashCode不等都不成立。 


所以,如果面试的时候,最好把hashCode与equals之间没有强制关系,以及根据(没有语法约束力的)规范的角度,应当做到...这两层意思都说出来:P



总结一下,equals()是对象相等性比较,hashCode()是计算对象的散列值,当然他们的依据是对象的属性。
对于equals,一般我们认为两个对象同类型并且所有属性相等的时候才是相等的,

在类中必须改写equals,因为Object类中的equals只是判断两个引用变量是否引用同一对象,如果不是引用同一对象,即使两个对象的内容完全相同,也会返回false。当然,在类中改写这个equals时,你也可以只对部分属性进行比较,只要这些属性相同就认为对象是相等的。 


对于hashCode,只要是用在和哈希运算有关的地方,前面提到了,和equals一样,在你的类中也应该改写。当然如果两个对象是完全相同的,那么他们的hashCode当然也是一样的,但是象前面所述,规则可以由你自己来定义,因此两者之间并没有什么必然的联系。 


当然,大多数情况下我们还是根据所有的属性来计算hashCode和进行相等性比较。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值