首页 > Java, 挨踢(IT) > 简易Java(15):Java中equals()hashCode()之间的联系

简易Java(15):Java中equals()hashCode()之间的联系

2014年11月14日 发表评论 阅读评论 352 人阅读    

Hash函数

Java的最基本的父类java.lang.Object中有两个非常重要的方法定义:

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

实践证明,正确理解这两个方法非常重要,尤其是向Map中添加用户自定义的对象时。即便如此,甚至有些高级开发人员有时很很难说清楚它们到底应该怎么用。在这篇文章里,我将先展示几个常见的错误例子,然后再解释这两个方法如何才能正确地配合。

1、一个常见错误

在下面的代码中,展示了一个常见的错误。

package com.diguage.books.simplejava.ar15;

import java.util.HashMap;
import java.util.Map;

/**
 * equals()方法的错误示例
 * <p/>
 * Coder:D瓜哥,http://www.diguage.com/
 * <p/>
 * Date: 2014-11-09 21:40
 */
public class Apple {
    private String color;

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

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Apple)) {
            return false;
        }

        if (this == obj) {
            return true;
        }

        return this.color.equals(((Apple) obj).color);
    }

    public static void main(String[] args) {
        Apple a1 = new Apple("green");
        Apple a2 = new Apple("red");

        //hashMap stores apple type and its quantity
        Map<Apple, Integer> m = new HashMap<Apple, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);

        System.out.println(m.get(new Apple("green")));
    }
}

在这里例子里,一个绿色苹果的对象被成功存储到HashMap中。但是,从这个Map中取出这个苹果时,这个苹果对象却找不到了。上面的程序输出为null。但是,在通过调试器,我们非常确定这个对象就存储在HashMap中。如下图:

Eclpse调试页面

2、hashCode()造成的问题

这个问题是由于没有覆盖hashCode()equals()hashCode()之间的契约是这样的:1)如果两个对象是相等的,那么它们必须有相同的哈希值(Hash Code);2)如果两个对象有相同的哈希值,那么它们未必一定相等。

Map的一种隐含意义就是比线性搜索能更快地找到对象。使用经过哈希计算过的键值去定位对象需要两步处理。在Map内部,对象被存在数组中的数组中。第一层数组的下标就是键的hashCode()返回值。搜索第二层数组是使用线性搜索,通过使用equals()来判断对象是否被找到。

D瓜哥注

这里对java.util.HashMap内部实现的描述并不是十分准确。根据源代码显示,第一层确实是数组,如果有Key的哈希值一样,即哈希冲突,则将其存为一个链表。感兴趣的小伙伴可以自己查看一下HashCode的源代码。

hashCode()Object类中的默认实现是,针对不同的对象,返回不同的整数值。因此,在上述例子中,不同的对象(即使是同一类型)有不同的哈希值。

哈希值就像一系列用于存储的仓库,不同的物品存放在不同的仓库里。相比将物品存放到同一个仓库,存放到不同地方会更加高效。因此,使用平均分散的哈希值是一个非常好的实践。(这不是本文重点,不再展开讲。)

解决办法就是在类中覆盖hashCode()方法。针对这个示例,我使用color字符串的长度即可。代码如下:

D瓜哥注

这不是好的实现方式,但是仅仅是针对本例已经足矣。

/**
 * 本例子中hashCode() 方法的的正确使用
 * <p/>
 * Coder:D瓜哥,http://www.diguage.com/
 * <p/>
 * Date: 2014-11-09 21:40
 */
public int hashCode() {
    return this.color.length();
}

《Simple Java》是一本讲解Java面试题的书。讲解也有不少独特之处,为了面试,《简易Java》走起!



作 者: D瓜哥,https://www.diguage.com/
原文链接:https://wordpress.diguage.com/archives/122.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分类: Java, 挨踢(IT) 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.