java中用==判断相等与equals方法的区别

Object类中的.equals()方法和.hashCode()方法详解

一、判断相等

​ 众所周知,在java中判断两个变量或者对象是否相等,可以使用 == 操作符或者‘.equals()’方法。而.equals()方法又涉及到.hashCode()方法,他们究竟的原理是什么,让我们细细道来。

基本类型的相等判断

​ 当判断基本类型的相等与否时,用‘==‘判断完全没问题,因为基本类型的相等与否完全取决于它的,在《java编程思想》一书中写道:“关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系”

​ 这看似简单的一句话其实告诉我们,用**==**这种基本操作去判断相等是一种基本操作,并没有“源码”,比如如下的代码:

import static java.lang.System.out;
public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        int n=3;
        int m=3;
        
        out.println(n==m);
        
        String str = new String("hello");
        String str1 = new String("hello");
        String str2 = new String("hello");
        
        out.println(str1==str2);
        
        str1 = str;
        str2 = str;
        out.println(str1==str2);
    }

}

​ 会输出 true,false,true

​ 结果很简单,最后一次判断让str1和str2指向了同一对象,所以他们就相等了。

总结== 操作符对于基本类型来说对比的是他们的值,而对于引用类型,对比的是他们的地址

对象类型的相等判断

​ 对于对象类型的相等判断,相信大家都知道用.equals()方法。

​ Object类的.equals()方法长下面这个样子:

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

​ 所以很显然,我们如果不重写Object类的.equals()方法,那么得到的结果和直接用 == 没什么区别!

二、重写.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;
    }

​ 以上是final的String类中重写的.equals()方法,它相当于先调用了super.equals()方法也就是直接用==判断,之后再确认了传进来的对象是字符串后,将其强制转换为字符串类型,再分割为char数组,进行逐位判断它的ASCII码是否相等。

​ 我们自己定义类的.equals()方法写法:

1	@Override
2	public boolean equals(Object obj) {
3		if (super.equals(obj)) {
4			return true;
5		}
6		if (obj == null) {
7			return false;
8		}
9		if (getClass() != obj.getClass()) {
10			return false;
11		}
12		/*
13		 * Model1 mol = (Model1)obj;
14			return (mol.code == code);
15		 */
16		return (obj.hashCode() == hashCode());
17	}

解读:

​ 1.@override 标签是非常建议加上的,方法重写的原则是方法名,返回值,以及参数个数和参数类型要完全和父类方法一致,否则你写的方法将不会覆盖掉父类的任何方法。然而如果你犯了这些错误,在你写了@override 标签后编译器将会报错。

​ 2.第3-5行调用了Object类的.equals()方法,当然你也可以写成上面那样。

​ 3.第6-11行,根据java语言规范要求,.equals()方法需要具有以下的特性:

1.自反性:对于任何非空引用x,x.equals(x)应当返回true。
2.对称性:对于任何引用x和y,当且仅当x.equals(y)返回true,y.equals(x)也应当返回true。
3.传递性:对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。
4.一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
5.对于任意非空引用x,x.equals(null)应该返回false。

​ 所以在涉及继承关系的两个类去进行equals判断时,第9行的

getClass() != obj.getClass()

​ 不能换成

obj instanceof 'ClassName'

​ 因为其不满足对称性。

​ 4.在第13行,意味着你已经确认了obj对象就是我们这个类的类型,所以可以通过强制类型转换将其转换回来,使用==比较基本类型域,使用.equals()方法比较对象域。

​ 注:在实现逻辑时,如果该类中有多个域将涉及到相等判断时,可以这么写:

return field1 == other.field1
	&& field2.equals(other.field2)
	&& field3.equals(other.field3)
	...

​ 不过更好的方法Objects.equals()方法是null安全的,写成如下:

return field1 == other.field1
	&& Objects.equals(field2,other.field2)
	&& Objects.equals(field3,other.field3)
	...

​ 5.这里的第16行是第13到15行的另一种写法,因为java开发规范还规定了:Equals与HashCode的定义必须一致,如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。

三、hashCode与.hashCode()方法

​ 先来介绍一下hashcode是个什么东东?

​ 首先,每个对象都有hashcode,Object类中的hashCode()方法得到的就是这个对象的hashCode,同时也是这个对象的存储地址。

​ 当我们重写了.hashCode()方法时,并不意味着我们改变了他们的存储地址,只是改变了返回的值(int)

String类的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;
    }

​ 以上是String类重写的hashCode()方法,它是根据字符串中每个字符的ASCII码计算得到的,所以:

String s = "Ok";
String t = new String("Ok");

​ 这两个字符串的hashCode是相等的,79 * 31 + 107 = 2556。

重写hashCode方法

@override
public int hashCode(){
    rerturn 7 * name.hashCode()
        + 11 * new Double(salary).hashCode()
        + 13 * hireDay.hashCode();
}

更好的方法,将采用null安全的Objects.hashCode()方法,以及使用Double.hashCode()避免创建Double对象。

@override
public int hashCode(){
    rerturn 7 * Objects.hashCode(name)
        + 11 * Double.hashCode(salary)
        + 13 * Objects.hashCode(hireDay);
}

更简洁的方法:

public int hashCode(){
    rerturn Objects.hash(name,salary,hireDay)
}

Objects.hash()的底层实现:

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}
public static int hashCode(Object a[]) {
    if (a == null)
        return 0;
        
    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

四、参考资料

[1](美)凯S.霍斯特曼,(Cay S.Horstmann)著,周立新,陈波,叶乃文,邝劲筠,杜永萍译.java核心技术 卷1 基础知识[M].机械工业出版社,2018年第10版,166-172页.

[2] 海子的博客,https://www.cnblogs.com/dolphin0520/p/3592500.html

[3] 最好的资料就是源码

猜你喜欢

转载自blog.csdn.net/PitBXu/article/details/83657135