深入详解Java中的hashcode()与equals()方法

Java中的超级父类 java.lang.Object 定义了两个重要方法:hashCode()与equals()方法。它们不仅被大量地直接使用,而且与集合容器,特别是与基于哈希机制的集合容器有着紧密的联系。本文中,我们首先提供一个常见的错误代码示例,接着分析错误原因,最后详细阐述 hashcode()equals() 方法之间的契约。

public boolean equals(Object obj);
public int hashCode();

常见错误

/**
 * 测试
 *
 * @author 杨小华
 * @create 2017-08-18 13:11
 */
public class Cat {
  private String color;

  public Cat(String color) {
    this.color = color;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) return false;
    if (!(obj instanceof Cat)) return false;
    if (obj == this) return true;
    return this.color.equals(((Cat) obj).color);
  }

  public static void main(String[] args) {
    Cat catA = new Cat("white");
    Cat catB = new Cat("black");
    HashMap maps = new HashMap<>();
    maps.put(catA, 10);
    maps.put(catB, 20);
    System.out.println(maps.get(new Cat("white")));
  }
}

我们可以仔细思考得出答案后,再运行代码获得答案,正确的答案是打印了 null 。我们来分析一下该代码的结构,在该代码中,一个白色的猫对象被成功地存储在HashMap容器maps中,当尝试从该maps中查找该白色的猫对象时,却没有发现该对象。但是,我们可以通过使用IDEA的debugger面板去检查HashMap容器maps,会发现白色的猫对象是已经存储在该maps中的。图片如下:

解决方案

造成错误的原因就在于Cat类只重写了equals()方法,没有相应地重写hashcode()方法。在Java中,一个类的equals()方法和hashCode()方法存在如下契约:

  1. 如果两个对象是相等的,那么他们的hashCode必须是相同的。
  2. 如果两个对象具有相同的hashCode,它们可以相等,也可以不相等。

总结起来就是一句话:如果重写了某类的equals()方法,那么肯定也需要重写该类的hashCode()方法,而且两个对象的equals()返回值相等的情况下,这两个对象的hashCode()方法的返回值也必须相等。

Java中的任何类,只有在遵守equals()方法与hashCode()方法之间的契约情况下,才能正确的作为各种基于哈希机制的集合容器的键,比如示例代码中的HashMap。通过使用哈希机制,HashMap的查找指定元素方法具有 O(1) 的时间复杂度,远远优于普通容器的线性时间复杂度。在HashMap中,通过哈希后的键去查找对应的值可以分为两个步骤。对于 Map 结构容器,我们可以看作成元素为数组的一个数组,元素在第一层数组的索引号就是其键的hashCode()值,在第二层数组中,元素使用其equals()来定位与判断该元素是否已存储。

对于Java中所有类的超级父类java.lang.Object而言,其hashCode()的默认实现是:对于不同的对象就返回不同的整型值。上述示例代码中,Cat类没有重写Object的hashCode()方法。所以,这条代码:

System.out.println(maps.get(new Cat(“white”)));

创建的Cat对象具有与catA不同的hashCode。

哈希机制的引入极大地提高了在集合容器中查找特定元素的速度。哈希码就像一系列的桶,不同哈希码的对象往往对应到不同的桶中。但是,不同的键值在经过哈希后,被对应到了同一个桶中,这取决于哈希函数的选取。好的哈希函数应该尽可能把不同键的值分配到不同的桶中。

最后,我们给出一个上述问题代码的解决方案,把下面的代码添加到Cat类中即可。

@Override
public int hashCode() {
return this.color.hashCode();
}

控制台正确打印: 10

你可能感兴趣的:(雷默,Java)