很多人都知道对象a,对象b a==b比较的是两者之间的内存地址,a.equals(b)=true 比较的是a与b的字符串内容。
但其实单单只知道这些只能说是初学者必须知道的东西,下面根据我浅薄的知识来给小白们扩充扩充,当然,欢迎大佬们来补充,我还有很多需要学习的地方,若有不对的地方,请各位帮忙指出,谢谢大家!!
具体的说,对于基本类型来说==比较的是两者的值,对于对象类型==比较的是内存地址。
int a=1;
int b=1;
Integer c=new Integer(2);
Integer d=new Integer(2);
System.out.println(a==b);//返回true
System.out.println(c==d);//返回false
为了更好的引申出equals方法,在这里我定义了一个类部类User,通过new出两个实例来比较
java的equals()方法,我们可以从源码得知equals()存在Object类,因为Object类是所有类的直接或间接的父类,所以我们可以知道equals()继承自Object类,通过源码我们可以看出,equals()的底层方法依赖的是==(可以参考下图1),也就是equals()原始方法与==是相同的,都是比较的内存地址,但是java提供的类中,大多都重写了equals(),使equals()比较的是两者对象的值。
图1
下面为了验证equals方法有被重写,我重写了equals()方法
输出结果:
由此也就真相大白了。
想知道两者之间的关系,我们先了解一下hashCode是什么,这里我从网上找了更权威的相关解释
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
从源码可以看到,hashCode源自Object类中,也就是java中所有类都有hashCode方法。 虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”(关于“散列表”见下面说明)时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。
上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。
也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
那么散列码是什么?
不必用冗长的线性搜索技术来查找一个键,而是用一个特殊的值,名为“散列码”。散列码可以获取对象中的信息,然后将其转换成那个对象“相对唯一”的整数(int)。所有对象都有一个散列码,而hashCode()是根类Object的一个方法...
实际的使用中,一个对象一般有key和value,可以根据key来计算它的hashCode。假设现在全部的对象都已经根据自己的hashCode值存储在不同的存储区域中了,那么现在查找某个对象(根据对象的key来查找),不需要遍历整个集合了,现在只需要计算要查找对象的key的hashCode,然后找到该hashCode对应的存储区域,在该存储区域中来查找就可以了,这样效率也就提升了很多。说了这么多相信你对hashCode的作用有了一定的了解,下面就来看看hashCode和equals的区别和联系。
Java对于eqauls方法和hashCode方法是这样规定的:
1 如果两个对象相同,那么它们的hashCode值一定要相同。也告诉我们重写equals方法,一定要重写hashCode方法,也就是说hashCode值要和类中的成员变量挂上钩,对象相同–>成员变量相同—->hashCode值一定相同。
2 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用eqauls方法比较。
对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!
这种大量的并且快速的对象对比一般使用的hash容器中,比如hashset,hashmap,hashtable等等,比如hashset里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equal()验证,如果hashCode()都不同,则肯定不同,这样对比的效率就很高了。
Person p1 = new Person(18);
Person p2 = new Person(18);
System.out.println(p1.equals(p2));//true
p1.equals(p2)返回true,是因为equals()被重写,而hashcode()没有被重写,p1.hashCode()==865113938,p2.hashCode()==1442407170,因此两者的hashCode不相同,违背了hibernate的原则 由此hibernate会产生错误判断,又以为它们不是同一个对象,因此我们还得重写hashCode方法。如何重写hashCode方法呢?
哈希码产生的依据:哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。
哈希码要完成这么一件事,首先要保证如果equlas出来的结果相等,那么hashCode也相等。像上面的u1和u2,由于名字都是“张三”,所以应该返回相同的hashCode。所以我们可以想一个办法。让Person的哈希码返回Person里面age字段的哈希码,这样就保证,年龄相同的人,不但equlas方法相同,而且hashCode相等。
重写之后:
class Person{
private String age;
public Person(String age) {
this.age = age;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Person)) {
return false;
}
Person p = (Person) obj;
if(this.age==p.age) {
return true;
}else {
return false;
}
}
@Override
public int hashCode() {
return age.hashCode();
}
这样可以保证hibernate根据我们自己的需求来判断重复对象。
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
Person p1 = new Person("17");
Person p2 = new Person("18");
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
结果:
false
1574
1575