熟悉又陌生的equals 与 hashCode

2019-06-11  本文已影响0人  imkobedroid

代码预热

先来看一段代码:


public class Main {

    public static void main(String[] args) {
        Person person1 = new Person("小明", 10);
        Person person2 = new Person("小张", 20);

        if (person1.equals(person2)) {
            System.out.print("小明其实就是小张");
        } else {
            System.out.print("他们是两个人");
        }
    }
}

答应出来的结果是:

他们是两个人

这里可以看出他们对比出来的不是同一个对象

源码分析

走到equals里面看源码如下:

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

这里采用的是==号,

" == "操作符主要比较的是操作符两端对象的内存地址。如果两个对象的内存地址是一致的,那么就返回 true ;反之,则返回false

8中基本数据类型分别为byte,short,int,long,float,double,char,boolean除了 float 和 double 外,其他使用 == 进行数值比较

看到这里应该能明白上边的等号其实就是一组数值的比较,但是明明是两个对象哪里来的数值进行比较的呢?其实不然:

当对象用 == 进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个 对象,他们的比较后的结果为 true,否则比较后结果为 false

java中所有的类都是继承于一个基类Object,这个基类里面就有这个equals方法,这个方法就是来提供对比两个对象的地址信息的

对象的hashCode

先看一段代码:

public class Main {

    public static void main(String[] args) {
        Person person1 = new Person("小明", 10);
        Person person2 = new Person("小张", 20);

        int hash1=person1.hashCode();
        int hash2=person2.hashCode();

        if (hash1==hash2) {
            System.out.print("小明其实就是小张");
        } else {
            System.out.print("他们是两个人");
        }

    }
}

结果如下:

他们是两个人

简要说明:这里因为hash1不等于hash2所以两个对象肯定不相等所以返回如上结果,但是这个如果相等的话是不是就说明两个对象是同一个对象呢?答案是:不一定

其中hashCode的源码如下:

 @HotSpotIntrinsicCandidate
    public native int hashCode();

native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。

他是一个本地的方法并且返回一个int型的数,但是这个是不是就是对象在内存中的地址呢?我们再看下面一段代码:

public class Main {

    public static void main(String[] args) {
    
        String s1 = "hello";
        String s2 = "world";
        String s3 = "helloworld";
        String s4 = s1+s2;
        System.out.println(s3==s4);
        System.out.println(s3.hashCode());
        System.out.println(s4.hashCode());
        System.out.println(System.identityHashCode(s3));
        System.out.println(System.identityHashCode(s4));
    }
}

结果是这样的:

false
-1524582912
-1524582912
2088051243
1277181601

因为s3==s4返回的是一个false说明他们在内存中的地址是不一样的不是同一个对象。但是他们的hashcode返回的数值却是一样的,这就说明了hashcode并不是内存中的实际地址,要想获得内存中的地址必须使用System.identityHashCode来获取。但是hashcode是什么呢?我们继续往下分析

hashCode的作用

官方文档对hashcode的定义是这样的

hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点。

hashCode 的常规协定是: 
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上equals比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。如果根据equals(Object)方法,两个对象是相等的,那么在两个对象中的每个对象上调用hashCode方法都必须生成相同的整数结果。 以下情况不 是必需的:如果根据equals(java.lang.Object)方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是Java编程语言不需要这种实现技巧。) 

感谢理解Java中hashCode的作用一文。

其实这里可以这样理解,以java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样

  1. new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
  2. 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal

总结

到这里我们再来看上边的代码可以总结出:

  1. 当我们用equals时比较的是两个对象的实际地址,这个很精确的可以判断出两个对象是不是同一个对象
  2. 当我们使用==号时比较的是数值是否相同
  3. 当我们使用hashcode的时候比较的是两个对象在hash表中计算出来的位置,当两个hashcode相同的时候并不能代表是同一个对象,两个不同的对象计算出来的hashcode可以相同,简称hash冲突,这个时候需要对比是否相同还是需要使用equals,但是不相同的时候肯定不是同一个对象,利用hashcode是为了提高了取对象的效率!
上一篇 下一篇

猜你喜欢

热点阅读