hashCode与equals联系和区别
来源:
java编程思想
Effective java
http://blog.csdn.net/fenglibing/article/details/8905007 http://blog.csdn.net/afgasdg/article/details/6889383
总结
1、java对hashCode和equals的规定:
如果两个对象相同,那么它们的hashCode值一定要相同;
如果两个对象的hashCode相同,它们并不一定相同(上面说的对象相同指的是用eqauls方法比较。)
你当然可以不按要求去做了,但你会发现,相同的对象可以出现在Set集合中。同时,增加新元素的效率会大大下降。
3、如下图所示,在集合(set)容器存入对象时,先判断hashCode是否相同,不相同直接放入,不判断equals,hashCode相同,再判断equals不满足的时再放入对象。
测试
- 不按照java规定的equals 与 hashCode写法书写,以便测试: 如下图所示,一个学生有四个属性,姓名、年龄、邮箱、id,equals判断如果姓名相同为true, hashCode定义为id的一个单射。
private String name;
private int age;
private String email;
private int id;
public Student(String name, int age, String email, int id) {
this.name = name;
this.age = age;
this.email = email;
this.id = id;
}
public boolean equals(Object student) {
return student instanceof Student && ((Student) student).name.equals(this.name);
}
public int hashCode() {
return (id * 37 + 171);
}
我们输入了姓名相同的三个张三,发现比较这三个对象时两两相同,因为调用了equals方法,只要姓名相同就相同,这三个对象有两个不同的id,当我们放入到set中发现只有两条记录,为id不同的两个,id相同的代表hashCode相同,并且满足equals,所以只有一条,hashCode不同还没有判断equals的条件,直接就存入了。
Student student1 = new Student("zhangsan", 14, "[email protected]", 1314141);
Student student2 = new Student("zhangsan", 14, "[email protected]", 144444);
Student student3 = new Student("zhangsan", 13, "[email protected]", 1314141);
System.out.println(student1.equals(student2));
System.out.println(student1.equals(student3));
Set<Student> set = new HashSet<>();
set.add(student1);
set.add(student2);
set.add(student3);
System.out.println(set.size());
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);set.forEach(System.out::println);
输出结果:
true
true
2
com.sunchenglong.javalearn.Student@2e5ef1c
com.sunchenglong.javalearn.Student@518d57
com.sunchenglong.javalearn.Student@2e5ef1c
com.sunchenglong.javalearn.Student@518d57
com.sunchenglong.javalearn.Student@2e5ef1c
源代码分析
Object.class 以前一直不知道toString出来的东西是什么,现在终于明白了。。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Object类的hashCode是一个native方法
public native int hashCode();
Object hashCode返回的并不一定是对象的(虚拟)内存地址,具体取决于运行时库和JVM的具体实现。
String hashCode的实现:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
一些理论:
一、equals满足的条件
正确的equals满足下面5个条件:
- 1. 自反性: x.equals(x)一定返回true;
- 2. 对称性: x.equals(y) == y.equals(x)
- 3. 传递性: x.equals(y) ==true/false y.equals(z) == true/false,那么x.equals(z) == true/false
- 4. 一致性,如果对象在计算过程中未发生改变,x.equals(y)执行多少次都不应该改变
- 5. 如果,x不是null,那么x.equals(null) = false
二、Effective java推荐的hashCode实现方式:
- 1. int result = 一个非0值
- 2. 为对象的每个有意义的域设置一个int的散列码 | 域类型| 计算| | -- | -- | | boolean|c=(f?0:1)| |byte,char,short,int|c = (int)f| |long| c = (int)(f^(f>>>32))| |float|c = Float.floatToIntBits(f);| |double|long l = Double.doubleToLongBits(f); c=(int)(l^(l>>>32)| |Object|c = f.hashCode()| |数组|每个元素应用上述规则|
- 3. 合并计算的散列码 result = 37* result + c;
- 4. 返回result
- 5. 确定相同的对象有相同的散列码