带你彻底理解hashcode、equals 和 ==

/ Java / 0 条评论 / 800浏览

下面的解读都是基于我对jdk的官方注释的理解,有不准确的地方欢迎下方指出

hashcode是为该对象返回一个哈希码,目的是让每一个对象都可以有一个唯一且不同的属性值.这样可以方便在java中使用散列表(也就是哈希表),因为hash表的key需要尽量不要出现冲突,而在java中我们一般也是需要使用对象作为key,所以这就需要我们为对象规定一个判断是否相同的标准.

Object中原始的hashcode方法是通过将该对象的存储地址值转为一个int数来作为哈希码返回(因为每一个对象的内存地址都不一样)

另外还有这样一个规范:

  1. 如果equals方法判断后相同,那么两个对象相同.那么hashcode一定要相同
  2. 两个对象相同,hashcode一定相同,equals一定相同
  3. 两个对象不同,hashcode一定不同
  4. hashcode相同,两个对象不一定相同
 ❶hash值不同,对象一定不同
 ❷hash值相同,对象不一定相同
            比如我们的key现在是Integer,Integer类中是这样重写equals和hashcode的:
            public static int hashCode(int value) {
        return value;
    }
            public boolean equals(Object obj) {
                if (obj instanceof Integer) {
                    return value == ((Integer)obj).intValue();
                }
                return false;
            }
            也就是,如果两个Integer对象的数字相同,那么hashcode一定相同,并且equals其实也就是比较hashcode,所以也相同,所以true&&true所以就是相同的对象
            如果是String对象,我们来看一下String类重写的hashcode和equals方法:
            public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
        //挨个比较每一个字符,所以只要字符串一样,那么一定hashcode也是一样的
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        //也是比较每一个字符,所以如果是相同的字符串,那么equals肯定是true
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    所以是true&&true,如果String a = "武汉加油";String b = "武汉加油";使用这两个依次作为key的话,也就是在进行判断的时候会作为两个相同的对象的(其实底层jvm还是有字符串常量池,这里不多讲)
    但是如果是我们自己定义的对象作为key,比如:
    Class Student()
    {
    String name;
    int id;
    }
    这样一个类,那么,如果我们没有重写这个对象的hashcode和equals方法,那么就会默认使用继承自Object类的
    也就是这样:
public boolean equals(Object obj) {
        return (this == obj);
    }
public native int hashCode();
从Object中上面的两个方法的注释中可以知道,equals是使用==比较两个对象,而我们知道==两边如果是引用数据类型,那么比较的就是其引用地址,其实也就是使用的hashcode方法,比较他们的
hashcode返回值,因为Object中的hashcode注释也说到:
        As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     *也就是可以看成是比较地址值

所以综上,加入现在我们需要使用上面的Student类的对象作为key,如:
Student stu1 = new Student("张三","666");
Student stu2 = new Student("张三","666");
刚开始put(stu1,"1");这个是可以存进去HashMap中,不会发生冲突;
之后我们put(stu2,"2");这个时候其实我们也是可以存进去的;
我们回到hashmap的冲突判断的if:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
它是先判断hash值是否相同,因为Student类没有重写hashcode方法,这是两个对象,他们在堆内存中的地址是不一样的,所以hash值不一样,所以直接判断是两个不同的对象,但是事实上,这两个对象
是一样的,都是代表学号是666这个张三同学,所以这就出现问题了,所以我们需要重写hashCode方法,比如,让hashcode方法返回一个固定的数1;
那么hash值一样,这样就会比较后面的,这个时候两个对象==,看这两个对象是否相等,也就是比较地址值,是不一样的,false,之后比较equals,我们重写了equals,比如是比较学号是否一样,
发现是一样的,true,所以是true&&true,所以是相同的对象
综上:
1.Object中hashcode方法返回的是对象的地址值,hashcode()表示的是JVM虚拟机为这个Object对象分配的一个int类型的数值,是一个jni的方法,调用的本地的c;
Object中的hashcode方法,目的是为了使每一个创建的对象调用这个方法可以返回一个都不一样的整数,因为每次创建一个对象,我们判断他们是不是相同的就==,这个时候比较的就是他们的引用地址,
但是他们在堆内存中的地址是不一样的,所以肯定不是相同的对象,所以hashcode方法其实底层就是利用将对象的内部地址值转变为一个int整数返回出来,官方的注释中还指出,这个实现不是java语言锁必须的,
实际上是底层的c和c++,有关Object.c和jvm.cpp;
2.Object中的equals()方法内部使用的是==,所以比较两个对象的时候,其实就是比较地址值了;
3.重写equals方法后为什么一到要重写hashcode
因为从Object类中,可以知道java中有这样一个约定,如果两个对象相同那么hashcode一定相同,如果不同hashcode一定不同,
所以当我们在存入hash表的时候,equals如果重写了,判断了如上面的stu1和stu2其实是相同的对象,但是如果没有重写hashcode方法,那么就会造成hash值不同(地址转的整数),所以就会出现判断
其实这两个对象是不同的,这样就会出问题了;
4.但是根据下面这个if判断,其实我们也看出来,其实equals是具有最终决定权,但是hashcode具有一票否决权!