hashCode
hashCode
对于包含容器类型的程序设计语言来说,基本上都会涉及到
当向集合中插入对象时,如何判别在集合中是否已经存在该对象,也许大多数人都会想到调用
通俗来说,
字符串中的hashCode
String new("String")
方式创建的两个相同字符串内容的对象他们的
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
public class IdentityHashCodeTest {
public static void main(String[] args) {
//下面程序中s1和s2是两个不同对象
String s1 = new String("Hello");
String s2 = new String("Hello");
//String重写了hashCode方法——改为根据字符序列计算hashCode值,
//因为s1和s2的字符序列相同,所以它们的hashCode方法返回值相同
System.out.println(s1.hashCode() + "----" + s2.hashCode());
//s1和s2是不同的字符串对象,所以它们的identityHashCode值不同
System.out.println(
System.identityHashCode(s1) + "----" + System.identityHashCode(s2)
);
String s3 = "Java";
String s4 = "Java";
//s3和s4是相同的字符串对象,所以它们的identityHashCode值相同
System.out.println(
System.identityHashCode(s3) + "----" + System.identityHashCode(s4)
);
}
}
/*
69609650----69609650
13078969----3154093
28399250----28399250
*/
在有些情况下,程序设计者在设计一个类的时候为需要重写
class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
/* 先注释掉对于hashCode的复写
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode()*37+age;
}
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
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<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p1, 1);
System.out.println(hashMap.get(new People("Jack", 12)));
}
}
在这里我只重写了
虽然通过重写System.out.println(hashMap.get(new People("Jack", 12)));
这句中的 new People("Jack", 12)
生成的是两个对象,它们的存储地址肯定不同。
hashCode 设计
一个好的
说到这里,你可能会想,原来构造
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
下面这段话摘自
- 在程序执行期间,只要
equals 方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode 方法必须始终如一地返回同一个整数。- 如果两个对象根据
equals 方法比较是相等的,那么调用两个对象的hashCode 方法必须返回相同的整数结果。- 如果两个对象根据
equals 方法比较是不等的,则hashCode 方法不一定得返回不同的整数。
对于第二条和第三条很好理解,但是第一条,很多时候就会忽略。在《
设计
hashCode() 时最重要的因素就是:无论何时,对同一个对象调用hashCode() 都应该产生同样的值。如果在讲一个对象用put() 添加进HashMap 时产生一个hashCdoe 值,而用get() 取出时却产生了另一个hashCode 值,那么就无法获取该对象了。所以如果你的hashCode 方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode() 方法就会生成一个不同的哈希码
下面例子中我们展示下不合理的
class People {
private String name;
private int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return name.hashCode() * 37 + age;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
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<People, Integer> hashMap = new HashMap<People, Integer>();
hashMap.put(p1, 1);
p1.setAge(13);
System.out.println(hashMap.get(p1));
}
}
这段代码输出的结果为“null”,想必其中的原因大家应该都清楚了。因此,在设计
Links
- https://mp.weixin.qq.com/s/XMQ27dyGokAegjOnba7FJA
hashCode 为什么乘以31 ?深入理解hashCode 和hash 算法