什么是hashcode()?我和你“==”吗?

614 阅读4分钟

equals和==

==操作符:

java的数据类型可以分为两种:

1.基本数据类型
  • byte(8位)、short(16位)、int(32位)、long(64位)、double(64位)、boolean、float(32位)、char(16位)---一个字节占8位
  • boolean特殊一点,1.JVM没有提供booolean类型专用的字节码指令,而是使用int相关指令来代替。2.对boolean数组的访问与修改,会共用byte数组的baload和bastore指令。

基本数据类型使用==就是比较他们的值是否相同

2.引用类型

==比较的是他们在内存中存放的地址,所以除非是同一个对象,否则就会返回false

对象是存放在堆上的,栈中存放的是对象的引用,“==”比较的是栈中的值,如果要比较堆中对象的内容是否相同,就需要重写equals方法

equals方法:

默认情况下(没有重写equals),equals比较的是对象的内存地址是否指向同一个地址,object对象默认的equsal方法等同于==

     public boolean equals(Object obj) {
        return (this == obj);
    }

如果重写了equals方法,就需要根据对象的内容是否相等来判断了

    //String类的equals方法
    public boolean equals(Object anObject) {
    
        //地址相同 返回true
        if (this == anObject) {
            return true;
        }
        //判断类型是否为String,否则返回false
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = length();
            //判断长度是否相同
            if (n == anotherString.length()) {
                int i = 0;
                //逐个字符进行比对
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

TextUtils工具类中的equals方法

    public static boolean equals(CharSequence a, CharSequence b) {
        //判断内存地址
        if (a == b) return true;
        int length;
        //判断空值和长度
        if (a != null && b != null && (length = a.length()) == b.length()) {
            //如果是String类型 调用String的equals方法
            if (a instanceof String && b instanceof String) {
                return a.equals(b);
            } else {
                //遍历字符进行比对
                for (int i = 0; i < length; i++) {
                    if (a.charAt(i) != b.charAt(i)) return false;
                }
                return true;
            }
        }
        return false;
    }

对比两种重写equals方法,可以发现,首先要对比的是“==”内存地址,其次判断对象是否为空以及类型是否一致,最后再根据具体条件进行比对

equals方法有几个特性需要关注一下

  • 自反性:a.equals(a) 返回true
  • 对称性:a.equals(b)返回ture那么b.equals(a)也返回true
  • 传递性:a.equals(b) b.equals(c) 那么 a.equals(c)成立
  • 一致性:如果参与比较的对象没有任何改变,那么比较的结果也不应改变
  • 非空性:任何非空的对象A A.equals(null)返回false

hashcode

1、hash和hash表是什么?

hash是一个函数,该函数中的实现就是一种算法,就是通过一系列的算法来得到一个hash值。这个时候,我们就需要知道另一个东西,hash表,通过hash算法得到的hash值就在这张hash表中,也就是说,hash表就是所有的hash值组成的,有很多种hash函数,也就代表着有很多种算法得到hash值。

2、hashcode

hashcode就是通过hash函数得来的,通俗的说,就是通过某一种算法得到的,hashcode就是在hash表中有对应的位置。

每个对象都有hashcode,对象的hashcode怎么得来的呢?

首先一个对象肯定有物理地址,在别的博文中会hashcode说成是代表对象的地址,这里肯定会让读者形成误区,对象的物理地址跟这个hashcode地址不一样,hashcode代表对象的地址说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址,那么对象如何得到hashcode呢?

通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode。所以,hashcode是什么呢?就是在hash表中对应的位置。

这里如果还不是很清楚的话,举个例子,hash表中有 hashcode为1、hashcode为2、(…)3、4、5、6、7、8这样八个位置,有一个对象A,A的物理地址转换为一个整数17(这是假如),就通过直接取余算法,17%8=1,那么A的hashcode就为1,且A就在hash表中1的位置。

3、equals方法和hashcode的关系?

  • 1、如果两个对象equals相等,那么这两个对象的HashCode一定也相同
  • 2、如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置

4、为什么equals方法重写的话,建议也一起重写hashcode方法?


class A {
    private String name;
    private int age;

    //重写equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof A)) return false;
        A a = (A) o;
        return age == a.age &&
                Objects.equals(name, a.name);
    }
    //重写hashCode
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

在重写了equals方法后,尽量也重写了hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。