java中“”==“” equals hashcode的关系

时间:2024-12-22 13:37:09

ava中的数据类型,可分为两类:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
2.复合数据类型(类)
当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。

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

如果对象没有覆写equals方法默认使用Object方法,就是比较对象的内存地址是否一样。

若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象
注意点:

如果类没有重写equals,那么对于该类的对象来说“==”和“equals”没有区别。都是比较对象的内存地址。 但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。所以会有很多经典的面试题:

String a = new String ("abc");
String b = new String ("abc");
System.out.println(a.equals(b));
System.out.println(a==b);

返回值是true和false

String以及基本数据类型的包装类中都重写了hashCode()方法,他们生成的哈希码是跟他们的内容
(这里就是指值)息息相关,也就是说在用equals()比较两个变量是否相等的时候只要他们的值相等,那么就返回true

第二:我们可以覆写覆盖equals()方法。则通过equals()比较该类的两个对象是否相等,但是需要注意的一点如果你重写equals()方法,那么你必须重写hashCode()方法

从开始学习Java,哈希码以及equals和==的区别就一直困扰着我。
要想明白equals和==的区别首先应该了解什么是哈希码,因为在jdk的类库中不管是object实现的equals()方法还是String重写的equals()方法以及
其它基本数据类型的包装类重写的euqals()方法,他们在比较对象的时候都是根据hashCode()方法返回的哈希码来判断两个对象是否相等的,所以要想搞清楚
equals()就必须要知道什么是哈希码。
那么究竟是什么哈希码呢?哈希码是可以根据的自己的需求,采用不同的算法产生的一个Int型数字。Object的hashCode()方法返回的哈希码是根据对
象的内存地址来生成的,所以每个对象的哈希码是不相同的,如果你要比较的两个变量的类型没有重写Object的hashCode()方法那么这两个变量除非是指向
相同的对象(地址相同),否则返回的一定是false。而String以及基本数据类型的包装类中都重写了hashCode()方法,他们生成的哈希码是跟他们的内容
(这里就是指值)息息相关,也就是说在用equals()比较两个变量是否相等的时候只要他们的值相等,那么就返回true,因为他们生成的哈希码相等。有个
值得注意的地方是:在JDK的类中只要重写的Object的equals()方法,那就肯定重写了它的hashCode()方法,因为equals()方法中在比较两个变量时,
判断的标准就是哈希码是否一样,Object中的hashCode()方法是根据对象的内存地址生成的,如果重写了equals()方法而继续使用原来的hashCode()方
法生成的哈希码作为判断相等的依据,那显然达不到我们要改变判断对象是否相等的标准的效果。
既然知道了什么是哈希码,现在就可以说明equals()和==的区别了,对于没用重写Object的equals()方法的类型所生成的对象的比较,equals()
和==是效果一样的,==比较的是两个变量所指向的对象在内存中指向的地址是否一样,而当两个变量的类型中继承了Object的equals()方法的时候,由于
该方法比较的标准是看哈希码是否相等,而哈希码是由hashCode()方法生成的,该方法生成哈希码的依据是对象在内存中的地址,最终比较的还是地址。所
以说equals()和==效果一样。而对于像String和那些基本数据类型的包装类来说equals()和==就不一样了,因为他们重写了Object的equals()方法和
hashCode()方法,使得equals()方法的判断标准发生了改变,他们的判断标准是看对象的内容是否相等,这里就是指值是不是一样,因为他们的哈希码是
根据对象的值生成的,与内存地址无关了,所以他们的equals()方法比较的是对象的值是否相等,而==比较的仍然是地址。所以equals()和==就不一样了。
这里还要注意一下,在比较值的时候,一般==比较的是基本数据类型,而equals()比较的是引用数据类型,地址相同一定值相等,而值相等地址不一定
相同。如果比较的是地址,那最好是用==,因为无论是否重写了Object的equals()方法,==永远比较的是地址,equals()比较的是哈希码,而哈希码生成
的标准是由类作者自己根据需求来控制的。

结论2:

1、如果两个对象相同,那么它们的hashCode值一定要相同;2、如果两个对象的hashCode相同,它们并不一定相同 ,上面说的对象相同指的是用eqauls方法比较。反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等
如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同

1、如果两个对象相等,那么它们的hashCode()值一定相同。
这里的相等是指,通过equals()比较两个对象时返回true。

2.、如果两个对象hashCode()相等,它们并不一定相等。
因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。

我们来看下面的例子

HashSet (需要重写hashCode和equals方法)

一般描述的事物需要往集合中添加,那么都需要重写这两个方法
删除和判断元素是否存在,都是先判断hashCode 看看是否存在,若存在则继续equals();

java中“”==“” equals  hashcode的关系

import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
public int hashCode()//重写
{
return name.hashCode()+age ;
}
public boolean equals(Object obj)//重写 Object不能换
{
if(!(obj instanceof Person))
return false;
Person p=(Person)obj; System.out.println(this.name+"...."+p.name); return this.name.equals(p.name)&&this.age==p.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class HashSetDemo
{
public static void main(String[] args)
{
HashSet hs=new HashSet(); hs.add(new Person("lisi01",30));
hs.add(new Person("lisi02",33));
hs.add(new Person("lisi03",35));
hs.add(new Person("lisi02",33));
hs.add(new Person("lisi01",30));
hs.add(new Person("lisi04",32));
hs.add(new Person("lisi03",35)); Iterator it=hs.iterator(); while(it.hasNext())
{
Person p=(Person)it.next();;
sop(p.getName()+" "+p.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj); }
}

参考案例2:

第一:
Set集合没有顺序,也不允许重复。
为什么要这样:模拟现实的集合。
这里的重复只是:对象的重复
何为对象的重复:指的就是同一个对象。
何为同一个对象:内存中,所在的内存编号一致。
内存编号的表示是什么:哈希码(哈希码一般是 类名 和 对象所在内存地址的十六进制数字表示 的组合)。
第二:
这种设置和实现中的矛盾在什么地方:
现实生活中只要属性相同,我们就认为那是同一个对象。
这与计算机比较同一个对象的方法不同(计算机使用内存地址,即哈希码)
于是,就需要重写equals方法和hashCode方法(&&)来让程序的运行结果符合现实生活
基本数据类型的实现类都已经重写了上面的两个方法。
第三:
为什么要重写equals方法和hashCode方法(技术实现原理):
程序向HashSet中添加一个对象时,先用hashCode方法计算出该对象的哈希码。
比较:
(1),如果该对象哈希码与集合已存在对象的哈希码不一致,则该对象没有与其他对象重复,添加到集合中!
(2),如果存在于该对象相同的哈希码,那么通过equals方法判断两个哈希码相同的对象是否为同一对象(判断的标准是:属性是否相同)
1>,相同对象,不添加。
2>,不同对象,添加!
这时有两个疑问:
1,为什么哈希码相同了还有可能是不同对象?
2,为什么经过比较哈希码还需要借助equals方法判断?

答:
首先:
按照Object类的hashCode方法,是不可能返回两个相同的哈希码的。(哈希码唯一标志了对象)
然后:
Object类的hashCode方法返回的哈希码具有唯一性(地址唯一性),但是这样不能让程序的运行逻辑符合现实生活。(这个逻辑就是:属性相同的对象被看作同一个对象。)

为了让程序的运行逻辑符合现实生活,Object的子类重写了hashCode的方法(基本数据类型的实现类都已经重写了两个方法,自定义的类要软件工程 师自己重写。)

那么:
重写的宗旨是什么?
重写就是为了实现这样的目的:属性相同的不同对象在调用其hashCode方法后,返回的是同样的哈希码。
但是
我们在重写的时候,发现几乎所有的写法都无法避免一个bug:有一些属性不同的对象(当然是不同的对象),会返回相同的哈希码。(即 重码)

最后:
为了解决这个问题:在哈希码相同的时候,再用equals方法比较两个对象的对应属性是否相同,这样,确保了万无一失。
这样:上面两个问题得到解决。
5
下面给出一个属性不同但哈希码相同的例子:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
class Person {
private String name;
private int id;
Person(String name,int id) {
this.name = name;
this.id = id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setId(int id){
this.id = id;
}
public int getId(){
return id;
}
public int hashCode(){
return name.hashCode()+id; //使用字符串哈希值与Integer的哈希值的组合
//这样会产生重码,实际上重码率很高
}
public boolean equals(Object obj){
if(obj instanceof Person){ //
Person p = (Person)obj;
return(name.equals(p.name) && id == p.id);
}
return super.equals(obj);
}
}
public class TestHashSet2 {
public static void main(String[] args) {
Person p1 = new Person("a",1);
Person p2 = new Person("b",0);
Set<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
Iterator<Person> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next().getName());
}
}
}
  • 总思路:hashCode不同时,则必为不同对象。hashCode相同时,根据equlas()方法判断是否为同一对象。
  • 在HashSet,HashMap,HashTable中都存在该问题。