Android面试题(1):java中==和equals和hashCode的区别

因为某些原因要重新找工作,这次打算系统的复习一下,找了一些BAT等大公司的面试题最全的BAT大厂面试题整理,这里记录一下。
另外就是,当开始复习的时候,我才发现,一个简单的问题里面涉及到的知识量可不少。
最简单的如标题所示的这个,看了这篇文章你会发现,原来并不是那么简单?如果面试官真的问到这个问题,我们更加深入的回答是不是更好呢。

1. 简单的概念性说法。

==是运算符,比较两个变量的值是否相同,八大基本数据类型(byte short int long float double char boolean)比较的是他们的值是相等。其他对象则是比较他们的地址值是否相等。

equals是比较两个对象是否相同,它在Object中的默认实现就是==。

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

hashCode(散列码)则是对象生成的一个散列码,它也是Object中的方法,对象的散列码是为了更好的支持基于哈希机制的Java集合类,例如 Hashtable, HashMap, HashSet 等。

2. 注意:覆盖equals方法时,也必须覆盖hashCode方法

以下内容摘自《Effective java》 更详细的问题请自行查阅。

Object规范[JavaSE6]约定内容:
如果两个对象根据equals方法比较时相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的正数结果。

如果不这样做,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,比如HashMap, HashSet, HashTable。

例如:

public final class PhoneNumber {

    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(short areaCode, short prefix, short lineNumber) {
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNumber = lineNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) 
            return true;
        if (o == null || getClass() != o.getClass()) 
            return false;
        PhoneNumber that = (PhoneNumber) o;
        return areaCode == that.areaCode &&
                prefix == that.prefix &&
                lineNumber == that.lineNumber;
    }

    // Broken - no hashCode method!!!
}

假设你其余将这个类与HashMap一起使用:

HashMap<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(707, 867, 5039), "Jenny");

这个时候,你可能期望m.get(new PhoneNumber(707, 867, 5039);会返回Jenny,但它实际上返回的是null。注意,这里设计两个PhoneNumber实例:第一个被用于插入到HashMap中,第二个实例实际上与第一个相等,被用于获取。
返回null是因为HashMap是以key的hashCode作为索引的,而两个PhoneNumber有两个不同的hashCode,所以无法找到对象。
解决方式就是让equls相同的两个PhoneNumber对象也返回相同的hashCode。需要注意的是,hashCode使用到的域要和equals的保持一致,确保符合Object规范[JavaSE6]约定。

3. 计算hashCode的方式

而计算hash值有个通用的公式可以使用 result = 31 * result + c,直接相加可能导致不同参数产生相同的结果(1+2 == 2+1),所以需要先乘以一个数后再相加,如下

  @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + areaCode
        result = 31 * result + prefix;
        result = 31 * result + lineNumber;
        return result;
    }

但是如果areaCode和prefix等这些参数不是int类型的时候,可能无法这么方便的使用,需要转换成int。
例如,如果areaCode是一个long类型,则需要这么转换(int)(f ^ (f>>32))

实际上Java就提供了计算HashCode的方法给我们:

@Override
public int hashCode() {
    return Objects.hash(areaCode, prefix, lineNumber);
}

源码如下:
Objects.java

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

Arrays.java

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;
}

可以看到源码也是使用 result = 31 * result + c这个公式。
那么31这个数字是怎么来的呢,为什么要使用它。

关于这点,主要有两个方面的原因:
1. 31是一个质数,习惯上都使用质素来计算散列结果。
2. 31可以用位移和减法来代替乘法,得到更好的性能:31 * i = (i << 5) - i。JVM会自动完成这种优化。

第一点涉及到hash算法的数学问题,这里就不多研究了,非要钻牛角尖的话可以自行搜索资料。

猜你喜欢

转载自blog.csdn.net/u010386612/article/details/79625307