在Java的Object类中有一个方法:
public native int hashCode();
根据这个方法的声明可知,该方法返回一个int类型的数值,并且是本地方法,因此在Object类中没有给出具体的实现。
hashCode方法的作用
hashcode是java中Objet类定义的方法,默认返回的是对象的内存地址(主要用途是在对对象进行散列的时候作为key输入)。但该方法的本意是散列。散列的话就必然涉及到在一定的空间中进行散列,所以hashcode方法一定是和集合配合使用的时候才用得到。
Java中的集合有两类,一类是List,另一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可以重复。
那么要想保证元素不重复,可两个元素重复应该依据什么来判断呢?
很多人都会想到equals方法来逐个进行比较,这个方法可行。但是如果集合中有数量庞大的数据,效率会成为一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果该table中没有该hashcode值,就可以直接存进去,不需要进行任何比较了。如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决问题,实际调用equals方法的次数就大大降低了。
equals方法与hashCode方法关系
- 如果两个对象equals,Java运行时环境会认为它们的hashcode一定相等。
- 如果两个对象不equals,它们的hashcode有可能相等。
- 如果两个对象hashcode相等,它们不一定equals。
- 如果两个对象hashcode不相等,它们一定不equals。
覆盖equals时总要覆盖hashCode
《Effictive Java》中提到:在每个覆盖equals方法的类中,也必须覆盖hashCode方法。如果不这样做,就会违反Object.hashCode的通用规定,从而导致该类无法结合所有基于散列的集合一起正常工作,这样的集合包含HashMap,HashSet和HashTable。
约定的内容是:
- 在应用程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
- 如果两个对象根据equals方法的比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生相同的整数结果。
- 如果两个对象的equals方法比较是不相等的,那么调用这两个对象中任意一个对象hashCode方法,则不一定要产生不同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的结果,有可能提高散列表的性能。
因没有覆盖hashCode而违反的关键约定是第二条:相等的对象必须具有相等的散列码。根据类的equals方法,两个截然不同的实例在逻辑上有可能是相等的,但是根据Object类的hashCode方法,它们仅仅是两个没有任何共同之处的对象。hashCode返回的两个看起来随机的整数,而不是像第二条规定所要求的的那样,返回两个相等的整数。
举一个例子:
package test;
import java.util.HashMap;
class People{
private String name;
private int age;
public People(String name,int age)
{
this.age=age;
this.name=name;
}
public void setAge(int age)
{
this.age=age;
}
@Override
public boolean equals(Object obj)
{
return this.name.equals(((People)obj).name)&&this.age==((People)obj).age;
}
}
public class Main {
public static void main(String[] args) {
People p1=new People("Jack",12);
System.out.println(p1.hashCode());
HashMap hashMap=new HashMap();
hashMap.put(p1,1);
System.out.println(hashMap.get(new People("Jack",12)));
}
}
输出结果:

重写了equals方法,认为只要姓名和年龄相等,两个People对象就会被认为同一人。但是输出却为null,这是因为在重写equals方法时忘记重写hashCode方法。hashCode方法是将对象的存储地址进行映射。p1指向的对象和new People("Jack",12)生成的是两个对象,存储地址肯定不同。put方法把一个People对象存放在散列桶中,get方法却在另一个散列桶中查找。
重写hashCode方法:
package test;
import java.util.HashMap;
class People{
private String name;
private int age;
public People(String name,int age)
{
this.age=age;
this.name=name;
}
public void setAge(int age)
{
this.age=age;
}
@Override
public boolean equals(Object obj)
{
return this.name.equals(((People)obj).name)&&this.age==((People)obj).age;
}
@Override
public int hashCode()
{
return name.hashCode()*37+age;
}
}
public class Main {
public static void main(String[] args) {
People p1=new People("Jack",12);
System.out.println(p1.hashCode());
HashMap hashMap=new HashMap();
hashMap.put(p1,1);
System.out.println(hashMap.get(new People("Jack",12)));
}
}
输出结果为:

hashCode方法用于散列存储,减少equals比较带来的效率问题。若两个对象equals相等,hashCode必须相等;不等时可能相等。覆盖equals时需同时覆盖hashCode,保证散列集合正确工作。未重写hashCode,即使equals相等,对象在散列集合中也无法找到。文章通过示例解释了不重写hashCode可能导致的问题。
&spm=1001.2101.3001.5002&articleId=100936724&d=1&t=3&u=7c5832d08ff74a11b9645d3080f140c5)
1万+

被折叠的 条评论
为什么被折叠?



