美文网首页
【Java】equals和hashCode

【Java】equals和hashCode

作者: 如雨随行2020 | 来源:发表于2022-01-18 01:40 被阅读0次
    1. 为什么要重写equals方法
      1)Object类中equals方法比较的是两个对象的引用地址,只有对象的引用地址指向同一个地址时,才认为这两个地址是相等的,否则这两个对象就不想等。
      2)如果有两个对象,他们的属性是相同的,但是地址不同,这样使用equals()比较得出的结果是不相等的,而我们需要的是这两个对象相等,因此默认的equals()方法是不符合我们的要求的,这个时候我们就需要对equals()方法进行重写以满足我们的预期结果。
      3)在java的集合框架中需要用到equals()方法进行查找对象,如果集合中存放的是自定义类型,并且没有重写equals()方法,则会调用Object父类中的equals()方法按照地址比较,往往会出现错误的结果,此时我们应该根据业务需求重写equals()方法。

    2. 为什么要重写hasCode方法
      1)hashCode()方法用于散列数据的快速存储,HashSet/HashMap/Hashtable类存储数据时都是根据存储对象的hashcode值来进行分类存储的,一般先根据hashcode值在集合中进行分类,在根据equals()方法判断对象是否相同。
      2)HashMap对象是根据其Key的hashCode来获取对应的Value。
      3)生成一个好的hashCode值能提高HashSet查找的性能,差的hashCode值不但不能提高性能,甚至可能造成错误。比如hashCode方法中返回常量,会让,HashSet的查找效率退化为List集合的查找效率;hashCode方法中返回随机数,会让查找结果变的不可预测。
      4)好的hashCode生成方式是让对象中的关键属性与质数相乘,并将积相加获取。

    3. 为什么java中在重写equals方法后必须对hashCode方法进行重写
      1)为了维护hashCode()方法的equals协定,该协定指出:如果根据 equals()方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode方法都必须生成相同的整数结果;而两个hashCode()返回的结果相等,两个对象的equals()方法不一定相等。
      2)在重写父类的equals()方法时,也重写hashcode()方法,使相等的两个对象获取的HashCode值也相等,这样当此对象做Map类中的Key时,两个equals为true的对象其获取的value都是同一个,比较符合实际。

    4. 重写equals方法
      重写equals方法需要遵循Java如下规则,否则编码行为会难以揣测:

      1. 自反性:对于任意的对象x,x.equals(x)返回true(自己一定等于自己);
      2. 对称性:对于任意的对象x和y,若x.equals(y)为true,则y.equals(x)亦为true;
      3. 传递性:对于任意的对象x、y和z,若x.equals(y)为true且y.equals(z)也为true,则x.equals(z)亦为true;
      4. 一致性:对于任意的对象x和y,x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也均为true,前提条件是没有修改x也没有修改y;
      5. 对于非空引用x,x.equals(null)永远返回为false。
        代码如下:
        @Override
        public boolean equals(Object o) {
        //我们知道o其实也只是引用
            if (this == o) return true; //表明两个引用相同,即都指向堆上同一个地址。
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Double.compare(person.score, score) == 0 &&
                    Objects.equals(name, person.name) &&
                    Objects.equals(dept, person.dept);  //dept是我们自定义的对象
        }
    

    注意:<font color = "red">如果成员变量是对象,那么该对象也必须重写equals。</font>
    我们进入Objects.equals方法中看

        public static boolean equals(Object a, Object b) {
            return (a == b) || (a != null && a.equals(b));
        }
    

    说明如果两个Person的某个属性为null,我们也认为在该属性上Person相等。

    1. 重写hashCode方法:
      HashMap对象是根据其Key的hashCode来获取对应的Value。
      在重写父类的equals方法时,也重写hashcode方法,使相等的两个对象获取的HashCode也相等,这样当此对象做Map类中的Key时,两个equals为true的对象其获取的value都是同一个,比较符合实际。

    重写hashCode()方法需要遵循hashCode()协定:

    1. 一致性:在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行hashcode比较时所用的信息没有被修改。
    2. equals:如果根据equals()方法比较,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode()方法都必须生成相同的整数结果,注:这里说的equals()方法是指Object类中未被子类重写过的equals()方法。
    3. 附加:如果根据equals()方法比较,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
      代码如下:
        @Override
        public int hashCode() {
            return Objects.hash(age, name, score, person);
        }
    

    只有简单的一行,最终会调用Arrays的hashCode方法

        public static int hashCode(Object a[]) {
            if (a == null)
                return 0;
            int result = 1;
            for (Object element : a)
                result = 31 * result + (element == null ? 0 : element.hashCode());
            return result;
        }
    

    这里中间有一层会把多个参数转成Object[]。然后循环调用数组中每个对象的hashCode去更新result。所以同样的,<font color="red">如果成员变量是对象,那么该对象也必须重写hashCode</font>。

    絮:

    1. 上面说调用Arrays的hashCode之前会有一层把多个参数转成Object[],但是我们知道age和score都是基本数据类型int和double,那么它们是如何转成Object的呢。
      很简单如我们所料,经过两步:自动装箱—>>向上自动转换,这些都是自动的,我们看下面例子,int自动装箱Integer,然后Integer向上自动转换Object
            int i = 2;
            o = i;
            System.out.println(o.getClass());
            /**output: class java.lang.Integer*/
    
    1. Integer的hashcode方法的实现
        @Override
        public int hashCode() {
            return Integer.hashCode(value);
        }
        public static int hashCode(int value) {
            return value;
        }
    

    直接返回其内的int值

    1. Double的hashCode方法实现
        @Override
        public int hashCode() {
            return Double.hashCode(value);
        }
        public static int hashCode(double value) {
            long bits = doubleToLongBits(value);
            return (int)(bits ^ (bits >>> 32));
        }
        public static long doubleToLongBits(double value) {
            long result = doubleToRawLongBits(value);
            // Check for NaN based on values of bit fields, maximum
            // exponent and nonzero significand.
            if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
                  DoubleConsts.EXP_BIT_MASK) &&
                 (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
                result = 0x7ff8000000000000L;
            return result;
        }
    

    一桶复杂的操作啊!!!

    转自:

    java重写equals()方法和hashCode()方法

    https://blog.csdn.net/panchao888888/article/details/80888592

    相关文章

      网友评论

          本文标题:【Java】equals和hashCode

          本文链接:https://www.haomeiwen.com/subject/mcyraltx.html