zoukankan      html  css  js  c++  java
  • Java 基础 equals,hashcode和==的区别

    总结  

    1.

    == vs equal()

    • ==是判断两个变量或实例是不是指向同一个内存空间,equals()是判断两个变量或实例所指向的内存空间的值是不是相同(覆盖以后,默认还是返回==的值
    • Object类的默认equals()方法,返回的依然是==结果。因此,如果不重写equals方法,== & equals()等效;如果覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)

    equal() vs hashcode()

    • Object.equals()不override的话,依然是返回==的结果
    • hashcode()是native方法:public native int hashCode();

    hashcode()的作用:Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢? 

    这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。   
    于是,Java采用了哈希表的原理。  这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

    2. String的字符串拘留

    • String s="abcd"
      • String s="abcd"是一种非常特殊的形式,和new 有本质的区别。它是java中唯一不需要new 就可以产生对象的途径。以String s="abcd";形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。 这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象, 如果没有,则在常量池中新创建一个"abcd",下一次如果有String s1 = "abcd";又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.
      • 可以这么理解: String str = "hello"; 先在内存中找是不是有"hello"这个对象,如果有,就让str指向那个"hello".如果内存里没有"hello",就创建一个新的对象保存"hello".
    • String s=new String("abcd")
      • 而String s = new String("abcd");和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。 就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。

    3. equals & hashcode 默认约定(前提:equals和hashcode都已经按照默认约定同步override了。这两个结论产生的原因,来自于“hashcode的作用”小节)

    • 如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等 
    • 如果两个对象hashcode不相等,他们一定不equals

    equals PK == 

    初步了解在JVM中的内存分配知识

            在JVM中,内存分为堆内存跟栈内存。他们二者的区别是: 当我们创建一个对象(new Object)时,就会调用对象的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用,当我们在后续代码中调用的时候用的都是栈内存中的引用。还需注意的一点,基本数据类型是存储在栈内存中。

    equals与==的区别 

    == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。

    ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较。

    ==指引用是否相同, equals()指的是值是否相同.用一张图可以简要的表现他们之间的关系:


    代码测试一:

    为什么ob1.toString().equals(ob2.toString() 结果是false

    public static void main(String[] args){
            Object ob1 = new Object();
            Object ob2 = new Object();
            /**
             * When Object.toString() not override, it would return
             * getClass().getName() + "@" + Integer.toHexString(hashCode()
             */
            System.out.println(ob1.toString());//"java.lang.Object@32d992b2"
            System.out.println(ob2.toString());//"java.lang.Object@215be6bb"
            System.out.println(ob1.toString().equals(ob2.toString()));//false
    
    
            String str1 = new String("");
            String str2 = new String("");
            System.out.println(str1.toString());//""
            System.out.println(str2.toString());//""
            System.out.println(str1.equals(str2));//true
    }
    

      

    String s="abcd" 与String s=new String("abcd")区别 

    String s="abcd"

    String s="abcd"是一种非常特殊的形式,和new 有本质的区别。它是java中唯一不需要new 就可以产生对象的途径。以String s="abcd";形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。 这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象, 如果没有,则在常量池中新创建一个"abcd",下一次如果有String s1 = "abcd";又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.

    可以这么理解: String str = "hello"; 先在内存中找是不是有"hello"这个对象,如果有,就让str指向那个"hello".如果内存里没有"hello",就创建一个新的对象保存"hello".

    String s=new String("abcd")

    而String s = new String("abcd");和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。 就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。

    代码测试二:

    equals PK hashcode

    源码对比

    equals

     /* @param   obj   the reference object with which to compare.
         * @return  {@code true} if this object is the same as the obj
         *          argument; {@code false} otherwise.
         * @see     #hashCode()
         * @see     java.util.HashMap
         */
        public boolean equals(Object obj) {
            return (this == obj);
        }
    

    hashcode

     /* @  return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
       public native int hashCode();
    

    该方法是个native方法,因为native方法是由非Java语言实现的,所以这个方法的定义中也没有具体的实现。根据jdk文档,该方法的实现一般是“通过将该对象的内部地址转换成一个整数来实现的”,这个返回值就作为该对象的哈希码值返回。哈希值一共有8位。

    hashcode简介

    hashCode()方法返回的就是一个数值,从方法的名称上就可以看出,其目的是生成一个hash码。hash码的主要用途就是在对对象进行散列的时候作为key输入,据此很容易推断出,我们需要每个对象的hash码尽可能不同,这样才能保证散列的存取性能。事实上,Object类提供的默认实现确实保证每个对象的hash码不同(在对象的内存地址基础上经过特定算法返回一个hash码)。Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。 哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。   

    散列函数,散列算法,哈希函数。
    是一种从任何一种数据中创建小的数字“指纹”的方法。
    散列函数将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。
    好的散列函数在输入域中很少出现散列冲突。
    =================================================================================
    所有散列函数都有如下一个基本特性:
    1:如果a==b,则h(a) = h(b)。
    2:如果a!=b,则h(a)与h(b)可能得到相同的散列值。

    Object 的hashCode方法:返回一个int类型

    public native int hashCode();  

    hashCode的作用

    想要明白hashcode的作用,必须要先知道Java中的集合。总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢? 

    这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。   
    于是,Java采用了哈希表的原理。  这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

    eqauls 与 hashCode 的默认约定

    前提条件是

    equals和hashcode都已经按照默认约定同步override了。如果没有按照默认约定override或者没有同时按照默认约定override,那另当别论。

    两条结论

    两条约定的原因,请看“hashcode的作用”一节。

    结论1:如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 (记这个)

    结论2:如果两个对象hashcode不相等,他们一定不equals。 (记这个)
    3.如果两个对象不equals,他们的hashcode有可能相等。(不要记这个) 
    4.如果两个对象hashcode相等,他们不一定equals。(不要记这个)  

    为什么覆盖equals时总要覆盖hashCode 

    因为结论1:如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 (记这个)


     一个很常见的错误根源在于没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable。

    1.在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

    2.如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

    3.如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生相同的整数结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

    版权声明

    作者:MrBoringBigFish 
    来源:CSDN 
    原文:https://blog.csdn.net/qq_36522306/article/details/80550210 
    版权声明:本文为博主原创文章,转载请附上博文链接! 

  • 相关阅读:
    【纯水题】POJ 1852 Ants
    【树形DP】BZOJ 1131 Sta
    【不知道怎么分类】HDU
    【树形DP】CF 1293E Xenon's Attack on the Gangs
    【贪心算法】CF Emergency Evacuation
    【思维】UVA 11300 Spreading the Wealth
    【树形DP】NOI2003 逃学的小孩
    【树形DP】BZOJ 3829 Farmcraft
    【树形DP】JSOI BZOJ4472 salesman
    【迷宫问题】CodeForces 1292A A NEKO's Maze Game
  • 原文地址:https://www.cnblogs.com/frankcui/p/10457847.html
Copyright © 2011-2022 走看看