Java基础(三):Java字符串比较之==,equals与 hashCode方法的区别与联系

概述

==: 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

equals:用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,String中的类是重写了object类里的equal()方法,而在原本的object类中,返回的是==的判断,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法。

hashCode : Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,它实际上是一个int型整数。

关系运算符==

基本数据类型

在Java中有八种基本数据类型:

  • 浮点型:float(4 字节), double(8 字节)

  • 整型:byte(1 字节), short(2 字节), int(4 字节) , long(8 字节)

  • 字符型: char(2 字节)

  • 布尔型: boolean

对于这八种基本数据类型的变量,变量直接存储的是“值”,基本数据类型之间的比较需要用双等号(==),因为他们比较的是值

引用数据类型

接口、类、数组等非基本数据类型

Java中的字符串String属于引用数据类型。因为String是一个类

引用类型的变量存储的并不是“值”本身,而是与其关联的对象在内存中的地址,当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。因为每new一次就会重新开辟一个新的堆内存空间

String s1 = "小新";
String s2 = "小新";
System.out.println(s1 == s2);   //true
String str1 = new String("小新");
String str2 = new String("小新");
System.out.println(s1 == str1); //false
System.out.println(str1 == str2); //false

s1 == s2为true,是因为s1和s2都是字符串字面值"小新"的引用,指向同一块地址,所以相等。

s1== str1为false,是因为通过new产生的对象在堆中,str1是堆中变量的引用,而s1是指向字符串字面值"小新"的引用,地址不同所以不相等。

str1 == str2为flase,是因为即使内容相同,由于不是指向同一个对象,也会返回false。

小结
  • 若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的是否相等
  • 若操作数的类型是引用数据类型,则该关系操作符判断的是左右两边操作数的内存地址是否相同。也就是说,若此时返回true,则该操作符作用的一定是同一个对象。

equals

来源

equals,是Objec类的方法,源代码如下:

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

可见Objec的equals方法,直接调用==,比较对象地址,即是否指向同一个对象。

不同的子类,可以重写此方法,进行两个对象的equals的判断。

String类源码中重写的equals方法如下:

public boolean equals(Object anObject) {   
    if (this == anObject) {     // 先判断是否为同一对象
        return true;
    }
    if (anObject instanceof String) {   // 再判断类型是否一致,
        // 最后判断内容是否一致.
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;
        while (n-- != 0) {
            if (v1[i++] != v2[j++])
            return false;
        }
        return true;
        }
    }
    return false;
}

使用equals方法,内部实现分为三个步骤:

  • 先比较引用是否相同(是否为同一对象),
  • 再判断类型是否一致(是否为同一类型),
  • 最后比较内容是否一致
equals 重写原则
  • 自反型,对于任何非null的引用值x,也就是自己等于自己x.equals(x)是true
  • 对称性,对于任何非null的引用值x和y,也就是x.equals(y)等于y.equals(x)
  • 传递性,对于任何非null的引用值x,y和z,如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么x.equals(z)也应该返回是“true”
  • 一致性,对于任何非null的引用值x和y,多次调用x.equals(y)得到的结果是一样的,只要引用没有被修改
  • 非空性,所有的对象都不能为空,对于任意非空引用x,x.equals(null)应该返回false。
小结

因此,对于 equals 方法:

  • Object类中equals方法本意是比较两个对象是否相同
  • 必要的时候,我们需要重写该方法,避免违背本意,且要遵循上述原则

hashCode

来源

hashCode 方法是基类Object中的 实例native方法,因此对所有继承于Object的类都会有该方法。

他的源码如下:

public native int hashCode();

说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、Integer、Double等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下:

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31 * h + val[off++];
        }
        hash = h;
    }
    return h;
}

想要弄明白hashCode的作用,必须要先知道Java中的集合。  
​ 总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?
​ 这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
​ 于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
​ 这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

hashCode()和equals()关系
  • equals()相等的两个对象,hashcode()一定相等;
  • hashcode()相等,那么equals()有可能相等,也有可能不等 ;
  • 如果hashcode()不等,equals()一定不相等;
  • 一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用 ;
  • 当一个对象类型作为集合对象的元素时,那么这个对象应该拥有自己的equals()和hashCode()设计,而且要遵守前面所说的几个原则。
为什么重写equals的同时也要重写hashCode?

1.使用hashcode方法提前校验,可以避免每一次比对都调用equals方法,提高效率

2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。

小结
  1. hashcode是系统用来快速检索对象而使用
  2. equals方法本意是用来判断引用的对象是否一致
  3. 重写equals方法和hashcode方法时,equals方法中用到的成员变量也必定会在hashcode方法中用到,只不过前者作为比较项,后者作为生成摘要的信息项,本质上所用到的数据是一样的,从而保证二者的一致性

参考资料

https://blog.csdn.net/justloveyou_/article/details/52464440#commentBox

https://www.cnblogs.com/aspirant/p/7079538.html

https://www.iteye.com/blog/bijian1013-1972404

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值