【Java基础专题】4. 讲一下equals()与hashcode(),什么时候重写,为什么重写,怎么重写?

本问题对应的项目链接:高频题回答模版训练

回答指导:先说在 Object 中,默认情况下是什么,之后关于为啥重写之类的,直接举例子,比如举 hashmap 的例子。

共以下 14 个回答

  • 八点上线 永久会员 2024年12月12日 上午2:25

    默认情况下equals是来比较,引用数据类型就比较地址是否相等,hashcode返回对象的哈希码,一般在实现自定义类的时候重写equals,也必须重写hashcode,在hashmap中,用hashcode决定键所在数组的位置,发生哈希冲突时用equals比较双方是否相同,因此equals相同hashcode必须也相同,这样等同的对象 就能映射到同一个位置

    0 赞同 0 条回复
  • Silence 永久会员 2023年10月21日 下午9:15

    嗯,首先我来讲一下这两个方法分别有什么作用。hashcode方法的话,它就是返回一个32位的哈希值来唯一标识这个对象。

    equals方法的话,它就是用来判断两个对象它是否完全相等。通常情况下是根据一个对象的某些属性和另一个对象的某些属性判断之后决定返回true或false

    如果说我们想要让我们自定义的类根据我们想要的逻辑来判断,来判断两个对象是否相等的话,我们就应该重写equals这个方法。我们在重写equals方法的同时,必须要求重写hashcode方法。

    之所以要这么规定的原因,主要是因为嗯我们在JAVA中的某一些集合,它的判断原理需要我们这么规定。

    比如说我们的hashmap在进行put的操作的时候,他首先会调用这个对象的key的hashcode的方法。然后把他的高位和低位预货之后再对这个数组的宽度进行取模,得到桶的下标。之后再判断,如果说这个桶它不为空的话,就跟这个桶中的元素的key的哈希值进行比较。如果说哈希值不相等的话,那么就不用判断了,直接判断下一个元素。如果说都没有相等的话,那么他就可以插入进去。如果说哈希值相等的话,那么它会进一步的调用equals来判断这两个对象究竟是否是同一个对象。如果说是的话,那么他就会覆盖他的value值。
    在这个put的过程中,他之所以可以根据哈西扣的不相等,就直接判断下一个的原因就是他已经提前知道了,我们的code不相等的话,那么调用equals返回也一定就是false,所以说我们重写哈西德的目的就是为了保证我们调用equals判断返回为true的情况下。他的哈西code一定是相等的。
    如果说我们只重写equals方法,不去重写哈希code的方法。那么就有可能导致说我们在put的一个元素的时候,它的key跟map中一个元素在逻辑上是相等的,但是hashcode却不相同,这就有可能导致在一个map中存入很多个相同的key。这样的话就破坏了hashmap他的key的唯一性的这样一个约定。就会造成一些混乱。所以我们要求在重写了equals方法之后,一定要重写hashcode的这个方法。
    嗯,这就是我对hashcode和equals的理解。

    0 赞同 0 条回复
  • LilyBoy 永久会员 2023年9月3日 上午10:30

    1、equal用于比较对象,hashcode()用于返回哈希码值;
    2、equal比较后相等,那么hashcode()的值一定会相等,hashcode相等,而equal不一定相等,插入对象时可以大大减少equal()比较的次数;
    3、举例子?

    0 赞同 0 条回复
  • 泡芙 永久会员 2023年6月27日 下午3:46

    equals() 与 hashCode() 是 Object 类的方法。默认情况下,equals()方法比较的是对象的引用相等性。而hashCode()方法也使用了默认的对象内存地址作为哈希码,导致相等的对象生成不同的哈希码。
    举个HashMap的例子:假设我们自定义了一个Person类,该类有name和age两个字段。我们希望在HashMap中以Person对象作为键,并根据name和age来判断两个Person对象是否相等。这时,我们就需要重写Person类的equals()方法和hashCode()方法,以实现基于内容的比较和哈希码计算。重写equals()方法时,比较name和age字段的值;重写hashCode()方法时,根据name和age字段计算哈希码值。
    通过重写equals()方法和hashCode()方法,我们可以在HashMap中正确地判断Person对象的相等性,并在哈希表中正确地存储和检索Person对象。
    需要注意的是,如果类不会被用作集合类的键或元素,或不需要比较对象的相等性,就无需重写equals()和hashCode()方法。

    class Person {
        private String name;
        private int age;
    
        // 构造函数和其他方法省略
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
            Person other = (Person) obj;
            return name.equals(other.name) && age == other.age;
        }
    
        @Override
        public int hashCode() {
            int result = name.hashCode();
            result = 31 * result + age;
            return result;
        }
    }
    
    /**
    * 这里的数字为什么要写 31, 这不是绝对的规定,它适用于一般情况下的对象。
    * 对于特定的对象和需求, 可能需要根据具体情况选择不同的乘数或其他哈希算法。
    *《Effective Java》里解释:
    * 之所以使用 31, 是因为它是一个奇素数。
    * 如果乘数是偶数, 并且乘法溢出的话, 信息就会丢失,因为与 2 相乘等价于移位运算(低位补0)。
    * 使用素数的好处并不明显, 但是习惯上使用素数来计算散列结果。
    * 31 可以表示为 (32 - 1), 用移位和减法来实现乘法运算效率更高。
    * 总之, 选择数字 31 作为乘法因子是为了在哈希码计算中引入更好的分布和均匀性, 从而减少哈希冲突的可能性。
    */
    
    0 赞同 0 条回复
  • 中国用户 永久会员 2023年6月26日 下午5:13

    默认情况下,equals()比较的是对象引用是否相等,hashcode()返回对象的内部地址;实际中,通常要比较内容是否相等,这时候就需要重写equals方法了。在HashMap、HashSet中,为了确保正确的存储,需要重写equals()与hashcode()。

    0 赞同 0 条回复
  • 。 永久会员 2023年6月17日 下午8:17

    equals()用来比较两个对象是否相同,默认比较对象地址,如果是基本数据类型则比较值,hashCode返回对象的哈希值,在需要把对象作为key来使用map或set时,需要同时重写equals和hashCode方法,equals方法为true则hashCode相等,通过hashCode值得到存储地址。如果只重写equals(),表中可能会有多个equals()互为true的key。

    0 赞同 0 条回复
  • upward ᯤ⁶⁰ᴳ 永久会员 2023年6月3日 上午9:00

    在使用底层数据结构为hashmap的时候散列集合等比如hashmap,hashset的时候重写。hashcode默认是地址引用的hash值,equals默认是比较地址的。hashcode层用到了hashcode计算节点hash值,出现hash冲突时,判断equals方法或者地址,如果相等就覆盖节点或者不加入。

    0 赞同 0 条回复
  • 小鱼 永久会员 2023年5月30日 下午8:59

    equals( ) 和hashCode( ) 是 Object 类的方法,equals( )默认是比较引用类型的地址值,在想要比较数值相同时候,重写。hashcode,方法能生成一个hash值,然后根据值是否相同来做一些事情。在比如集合那块,hashset(底层是hashmap),要往table放的话,首先计算哈希值,转成索引值,没有直接放,有的话就要比较equals了,这时候equals也要重写,不重写可能也加进去了。就和我们的想法违背了。用快捷键重写。。。

    0 赞同 0 条回复
  • Gary 永久会员 2023年5月29日 下午2:20

    默认情况下,equals()比较的是两个对象的内存地址是否相同,而hashCode()是将对象的内容或值,经过哈希函数运算之后,返回一个int类型的结果。这两个方法都用于两个对象之间的比较。对于HashMap HashSet等集合来说,比较两个对象的大致流程是,先计算出两个对象的hashCode,如果hashCode不相等,说明这两个对象不相同;如果hashCode相等,就用equals方法再进行比较,判断这两个对象是否相同。
    一般来说,对于自定义的类,都需要对equals和hashCode这两个方法进行重写,保证集合在处理这些类的对象时,能够正确地存储和查找对象。
    重写这两个方法,使用Lombok的注解@Data,是比较简单的方式。
    对于equals方法,传进来一个Object类型的对象,如果这个对象为空,或者跟当前对象不是属于同一个类,直接返回false。再把传进来的对象,本来Object类型,强转为当前对象的类型,比较这两个对象的各个属性是否相等。
    对于hashCode方法,直接调用Objects类的hash方法,把这个类的所有属性传进去。

    0 赞同 0 条回复
  • mpweixin用户 永久会员 2023年5月25日 下午9:56

    hashcode()、equals()方法是Object类中的方法,Object类是所有类的父类,所以每个类都有这两个方法。
    在Object中,equals()方法是通过比较两个对象的在堆中的地址(引用地址)来比较两个对象是否相同,hashcode()方法是用来计算对象的hash值,但是在原生的Object类中的hashcode是根据引用地址来映射的。
    在实际应用的我们经常会对equals()和hashcode()方法进行重写,以达到我们判断两个对象的内容是否一致的效果。以我们的hashmap为例子,当我们在存入一个Object对象时,我们希望我们对象的内容一样时,我们就认定该对象是相同的,不能进行写入。此时如果我们使用相同的内容去new了像个对象,如果不进行重写的话,我们发现我们对比的就是两个对象的地址,我们new了两次那么两次的地址肯定是不一样的,会将两个对象放在不同的位置,达不到我们想要的结果。hashCode()用来定位要存放的位置,equal()用来判断是否相等,所以我们需要重写hashcode和equals,重写hashcode使得我们的hash值使用我们对象内容来映射,先找到对应的哈希表上是否有元素,如果有,然后重写我们的equals通过与该位置的对象的地址以及key、value值来进行比较两个对象是否相同,有任意的不同之处都证明我们不是相同的对象,可以插入,否则认为是相同的对象。

    0 赞同 0 条回复
  • Everglow 永久会员 2023年5月25日 上午12:54

    equals()和hashcode()都是Object类的方法,在没有重写的情况下,equals比较的是两个对象引用是否指向同一个对象,hashcode返回的是当前对象的地址经过hash函数映射得到的哈希值

    一般我们想用HashMap存储对象时需要重写这两个方法,来保证集合中不出现重复Key,这里重复Key的定义是对象的属性不能完全相等,所以我们需要重写equals,来逐一比较两个对象的属性值,但是一定也要重写hashcode,因为hashmap是先通过调用hashcode得到一个哈希值,将该哈希值按数组长度取模得到一个哈希桶的位置,再通过equals方法依次比较哈希桶上元素的key,所以同时重写这两个方法,得以保证集合中不会出现重复key,如果只重写hashcode的情况下就会造成一个哈希桶上出现相同key的元素,如果只重写equals的情况下就会造成相同元素的key出现在不同哈希桶的位置上

    对于hashcode方法最简单的重写方式就是依次调用对象中每一个属性的hashcode并相加。

    0 赞同 0 条回复
  • 翰林猿 永久会员 2023年5月24日 下午11:18

    因为散列集合插入对象时会进行判断,先调用hashcode,如果相同,再调用equals,如果都相同则只插入一个。

    如果只重写了 equals 方法,那么默认情况下,散列集合(HashMap、LinkedHashMap、HashSet、 LinkedHashSet )进行去重操作时,会先判断两个对象的 hashCode 是否相同,此时因为没有重写 hashCode 方法,所以直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是两个对象的引用地址,显然是不同的。所以结果是 false,那么导致 equals 方法就不执行了,直接返回的结果就是 false,所以两个对象不是相等的,于是就在散列集合中插入了两个相同的对象。(显然是错误的)

    但是,如果在重写 equals 方法时,也重写了 hashCode 方法,那么在执行判断时会去执行重写的 hashCode 方法,此时对比的是两个对象的所有属性的 hashCode 是否相同,于是调用 hashCode 返回的结果就是 true,再去调用 equals 方法,发现两个对象确实是相等的,于是就返回 true 了,因此散列集合就不会存储两个一模一样的数据了,于是整个程序的执行就正常了。

    总结
    hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的,采用这种方式的原因是可以提高程序插入和查询的速度,如果在重写 equals 时,不重写 hashCode,就会导致在某些场景下,比如将两个相等的自定义对象存储在散列集合时,就会出现程序执行的异常,为了保证程序的正常执行,所以我们就需要在重写 equals 时,也一并重写 hashCode 方法才行。

    2 赞同 0 条回复
  • 小肸 永久会员 2023年5月24日 下午9:19

    equals()方法是用来比较两个对象是否相等的。hashcode()是用来计算哈希码用来确定对象在哈希表中的位置的。比如你需要根据对象的内容来判断两个对象是否相等时,你就必须重写Object类的equals()方法,因为equals()方法Object类默认是根据对象的地址值是否相同判断两个对象是否相等。这时你就必须还要重写hashcode()方法,根据对象的内容计算哈希码,而不是根据对象的地址值计算哈希码。因为必须满足一个相等的对象的hashcode一定是相同的,但不相等的对象的hashcode可以相同,此时会产生哈希冲突。否则HashMap就不能正常使用。假设HashMap类的key重写了equals()方法,根据内容来比较两个对象是否相等,而没有重写hashcode()方法,默认使用Object类的hashcode()根据对象地址值计算哈希码。此时可能会出现一种情况:比如你put一个(key, value1)的键值对,后面又put一个(key, value2)键值对,然后你执行一个get(key)操作,取出来的值可能是value1而不是value2,为什么呢,因为这个key是第一个旧的key对象,你的key的hashcode是根据地址值计算的,而不是根据对象内容,两个key的内容虽然都是key,但地址值不一样,于是hashcode就不一样,所以(key, value1)和(key,value2)就位于不同的哈希槽上,于是HashMap中就出现两个内容相同的key。如果需要取出(key, value2)怎么办呢,你需要执行gey(key)(注意: key是第二个新的key对象,这样才能根据它的地址值计算出对应的哈希槽)。

    1 赞同 0 条回复
  • 秊 永久会员 2023年5月24日 下午3:58

    首先 equals( ) 是 Object 中的方法,默认是用 = = 来比较的。hashCode( ) 也是 Object 类的方法,根据一定的规则将与对象相关的信息,比如对象的内存地址,映射成一个数值,这个数值称作为哈希值。

    有时候我们想要自定义类的比较规则时,需要重写 equals( ),但是为了保证类在 HashSet 和 HashMap 等集合中的正确存储,也要同时重写 hashCode( ) 。

    以 HashMap 为例, HashMap底层在添加相同的元素时,会先调用两个对象的 hashCode( ) 是否相同,如果相同还会再用 equals( ) 比较两个对象是否相同。

    假设有一个 Person 类,有 name 和 age 两个字段,我们现在重写 equals( ) 规定只有两个 Person 的 name 和 age 都相同时,才认为两个 Person 相等。现在 new 出两个 name 和 age 都相同的 Person,分别添加到 HashMap 中。我们期望最后 HashMap 中只有一个 Person,但其实是有两个。原因在于添加第二个 Person 时,先比较的是两个 Person 的 hashCode( ),注意此时我们没有重写 hashCode( ) ,那么分别 new 出来的 Person 的哈希值肯定是不同的,到这里 HashMap 就会将两个 Person 认定为不同的元素添加进去。

    解决的办法就是重写 hashCode( ),最简单的返回 name 和 age 的哈希值的乘积即可。

    2 赞同 0 条回复

# 回答此问题

后才能回答