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不满足的时再放入对象。

测试

  1. 不按照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. 确定相同的对象有相同的散列码

results matching ""

    No results matching ""