【剪不断,理还乱】Java 中 equals 和 == 的那些事

细说 equals 和==的那些事

1,起源于 Object

我们都知道 Java 中所有的类都默认继承了 Object 这个超类,其中 Object 有 10 多个方法,这里我们介绍一下主要的toStringequalshashcode方法。

  • toSting方法默认返回“类名@地址值”;我们一般对它进行重写,然后就可直接打印对象得到输出重写后的形式。

下面重点介绍一下equalshashcode

  • equals方法默认比较对象(地址),即进行的是==运算。如果需要比较内容,需要进行重写。
  • hashcode默认是根据对象地址返回一串整数,也就是说地址相同hashcode就相同。而且hashcode是 Object 中的native方法,本地方法,表明该方法事调用了别的语言实现的,有点像接口,不用自己实现。

下面是 Object 类中的相关源码:

 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
public boolean equals(Object obj) {
        return (this == obj);
    }
public native int hashCode();

2,纠缠在 String

我们再来说一下java.lang.Stringequalshashcode的特点:

String 对equalshashcode都进行了重写。

首先 hashcode 是根据对象内容计算 hash,因此对象内容相同,他们的 hashcode 就相同。

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
			
            //这种 hash 计算方式不难看出,元素 value 相同 h 会是一样的
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

对于 string 而言,equlas 比较的是对象内容,这也是由于字符串的特殊性。

那么==比较的就是对象地址。

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,代码验证

下面是测试代码:

public class TEST {
    public static void main(String[] args){

        Object o1 = new Object();
        Object o2 = new Object();
        Object o3 = o2;
        System.out.println(o1==o2); //false
        System.out.println(o1.equals(o2)); //false,Object 原生 equals,默认比较地址
        System.out.println(o1.hashCode()==o2.hashCode()); //false,根据地址计算 hash
        System.out.println(o2.hashCode()==o3.hashCode()); //true

        String s1 = "abc";
        String s2 = s1; //常量池优化机制
        String s3 = new String("abc");
        System.out.println(s1==s3); //false 地址不一样,一个在常量池中一个在堆中
        System.out.println(s1.equals(s3)); //true,比较内容
        System.out.println(s2.hashCode()==s1.hashCode()); //true,根据内容计算 hash
        System.out.println(s1.hashCode()==s3.hashCode()); //true
    }

}

4,重写方法

有些场景中,我们需要自定义”相等“规则。比如我们定义对象的内容相同即为相等,此时需要重写 equals 方法:

import java.util.Objects;

public class Person {	
	private String name;
	private int age;
	
    @Override
    public boolean equals(Object o) {
        // 如果对象地址一样,则认为相同
        if (this == o)
            return true;
        // 如果参数为空,或者类型信息不一样,则认为不同
        if (o == null || getClass() != o.getClass())
            return false;
        // 转换为当前类型
        Person person = (Person) o;
        // 要求基本类型相等,并且将引用类型交给 java.util.Objects 类的 equals 静态方法取用结果
        return age == person.age && Objects.equals(name, person.name);
    }
}
 //对象比较地址,字符串比较内容
 public static boolean equals(Object a, Object b) {
 //1,对象类型时
 //a==b 为 true,则 true
 //a==b 为 false,则 false;
 //2,string 类型时
 //地址 true,内容 true,则 true
 //地址 false,内容 true,则 true;地址不等内容不等,flase
        return (a == b) || (a != null && a.equals(b));
    }

在有些场合,我们还需要重写 hashcode,比如 HashSet 中“元素相等”的判断机制就是综合考虑了 hashcode 和 equals 方法;所以当我们自定义对象时需要重写 hashcode 和 equals 两个方法。

equals 的方法重写参考上例,下面是 hashcode 方法的重写以及相应方法源码:

@Override 
public int hashCode() {
     //仍然调用的是 Objects 的 hash 方法
    //对于这里而言,age 走地址判断;name 走内容判断;所以只要他们内容相同就会返回相同的 hash
        return Objects.hash(name, age);
    }
public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }
public static int hashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;
		//仍然走的是 Object 的 hashCode 方法
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }

当然上述重写方法,idea 的 Generate 提供了重写模板,可一键生成。

5,总结

Object 的 hashcode 根据地址返回值,equals 比较地址,==比较地址;

String 的 hashcode 根据内容返回值,equals 比较内容,==比较地址。

即 Object 一律比较的是地址,由于 String 进行了方法重写,都是比较的内容。==其实本身就是比较地址。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值