zoukankan      html  css  js  c++  java
  • 关于 equals() 与 hashCode() 个人理解总结

    1:如果我们自定义的类没有重写 hashCode 和 equals() 方法的话 ?
    在使用 equals 对比的时候,是错误的!

    因为没有重写的类都是继承祖宗 Object 的  hashCode  和 equals 方法 , 这是Object 源码的 hashCode 和 equals 方法

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

    hashCode 方法有  native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C== 语言实现的,由java去调用.

    (对于还不理解hashCode 的人来说,先断言  Object 类 hashCode 返回的就是对象的存储地址,有助于理解)

    equals 方法直接对比的是  对象的内存地址 , 跟内容毫无关系!!! 

    实战案例: 

    package com.example;
    
    import lombok.Data;
    import org.junit.Test;
    
    import java.util.*;
    
    /**
     * @Author: qiuj
     * @Description:
     * @Date: 2019年8月6日17:22:06
     */
    public class HashCodeTest {
    
        @Test
        public void test4(){
            HashSet hashSet = new HashSet();
            hashSet.add(new Handsome(18,"邱健"));
            hashSet.add(new Handsome(18,"邱健"));
            Iterator iterator = hashSet.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
    
        }
    
        class Handsome{
            int age;
            String name;
    
            public Handsome(int age,String name){
                this.age = age;
                this.name = name;
            }
    
            public String toString(){
                return age + ":" +name;
            }
    
        }
    }
    

    我们用  Set 集合,集合内的元素不重复。结果返回false  。就是因为 Handsome 类没有重写 hashCode 和 equals 方法

    set 在添加第二个对象时,先把 hashCode 算出来 ,发现容器内并没有此 hashCode 的值。直接就放入容器内了。此时都还没有进行 equals 的对比 。 

    深入 hashCode 底层

    实际上:hashCode 不一定都是返回对象的存储地址,但是有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联

    下面是HotSpot JVM中生成hash散列值的实现:

    static inline intptr_t get_next_hash(Thread * Self, oop obj) {
      intptr_t value = 0 ;
      if (hashCode == 0) {
         // This form uses an unguarded global Park-Miller RNG,
         // so it's possible for two threads to race and generate the same RNG.
         // On MP system we'll have lots of RW access to a global, so the
         // mechanism induces lots of coherency traffic.
         value = os::random() ;
      } else
      if (hashCode == 1) {
         // This variation has the property of being stable (idempotent)
         // between STW operations.  This can be useful in some of the 1-0
         // synchronization schemes.
         intptr_t addrBits = intptr_t(obj) >> 3 ;
         value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
      } else
      if (hashCode == 2) {
         value = 1 ;            // for sensitivity testing
      } else
      if (hashCode == 3) {
         value = ++GVars.hcSequence ;
      } else
      if (hashCode == 4) {
         value = intptr_t(obj) ;
      } else {
         // Marsaglia's xor-shift scheme with thread-specific state
         // This is probably the best overall implementation -- we'll
         // likely make this the default in future releases.
         unsigned t = Self->_hashStateX ;
         t ^= (t << 11) ;
         Self->_hashStateX = Self->_hashStateY ;
         Self->_hashStateY = Self->_hashStateZ ;
         Self->_hashStateZ = Self->_hashStateW ;
         unsigned v = Self->_hashStateW ;
         v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
         Self->_hashStateW = v ;
         value = v ;
      }
     
      value &= markOopDesc::hash_mask;
      if (value == 0) value = 0xBAD ;
      assert (value != markOopDesc::no_hash, "invariant") ;
      TEVENT (hashCode: GENERATE) ;
      return value;
    }

      该实现位于hotspot/src/share/vm/runtime/synchronizer.cpp文件下。

    2:hashCode 的作用?

    hashCode 的作用在于 效率 二字,如上个案例,当我们放入第二个重复对象的时候。他先会去比较hashCode 值,如果不一致 ,直接说明两个内容不一致。放入容器内。如果 hashCode 一致,再去执行equals 方法 进行 一个一个 value 的对比。如果此时容器内有 1000 个 ,这时在添加1个元素 。 没有hashCode  那不是要对比  1000次 ,而且每次都一个一个 value 的对比。那效率可想而知!!!

    案例1:我们可以看看 String hashCode()的源码实现。  可以看出hashCode 的结果是固定的,并不是随机的。只要 值一致就会产生一样的结果。所以 以此说明如果 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;
        }

    案例2:String equals()的源码实现,可以看出先比较引用,然后就是完全的比较  每个字符了。这样保证内容都是一致的

        public boolean equals(Object anObject) {
            if (this == anObject) {
                return 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;
        }

    3:两个对象  hashCode 一致,他们完全相同吗?

    不一定,以 String 来说, hashCode 一致那么内容肯定是一致了。 equals 方法肯定也是 true 。但是他们附属在的对象内存不一致

    假例:第一个 hashCode =1018547642  内存地址=@1018547642  那第二个 hashCode 也是一样。内存地址也一样,但是该地址已经存放数据了, 就需要再次找到个没人占用的内存地址进行存储 。 不相同指出就在于  内存地址

    3:为什么重写了 equals()  ,还要重写 hashCode()

    (1):自定义对象没有重写  hashCode  ,那么放入  Set 集合类型的容器就会导致重复的元素放入到容器内。

    因为 Set 类型集合的执行顺序:

                                                     会先判断 hashCode  如果连  hashCode 都不一致 

                                                     那就不会执行 equals 方法了

    这也牵扯出另一个问题 , 就是效率 。 如果没有重写 hashCode ,每次都去 执行 equals 方法和容器内的所有元素统统对比一次。那程序的效率将导致非常低下!!!

    如有错误,恳请指出!

  • 相关阅读:
    ant构建Jmeter脚本的build文件配置(build.xml)
    Jmeter加密函数__digest总结
    Python接口自动化测试脚本-实现禅道登录
    转载:windows下安装mac虚拟机(Vmvare+mac)
    jstat监控JVM内存使用、GC回收情况
    Pycharm添加Python文件模板
    总结:Jmeter常用参数化方式
    Mysql添加索引及索引的优缺点
    Mysql常用语法
    性能测试中TPS上不去的原因
  • 原文地址:https://www.cnblogs.com/blogspring/p/14191766.html
Copyright © 2011-2022 走看看