2

我创建了一个类“书”:

public class Book {

public static int idCount = 1;

private int id;
private String title;
private String author;
private String publisher;
private int yearOfPublication;
private int numOfPages;
private Cover cover;

...

}

然后我需要覆盖hashCode()和 equals() 方法。

@Override
public int hashCode() {

    int result = id; // !!!

    result = 31 * result + (title != null ? title.hashCode() : 0);
    result = 31 * result + (author != null ? author.hashCode() : 0);
    result = 31 * result + (publisher != null ? publisher.hashCode() : 0);
    result = 31 * result + yearOfPublication;
    result = 31 * result + numOfPages;
    result = 31 * result + (cover != null ? cover.hashCode() : 0);

    return result;
}

equals() 没有问题。我只是想知道 hashCode() 方法中的一件事。

注意:IntelliJ IDEA 生成了 hashCode() 方法。

那么,将结果变量设置为 id 是否可以,或者我应该使用一些素数?

这里有什么更好的选择?

谢谢!

4

3 回答 3

5

请注意,只有结果的初始值设置为id,而不是最终值。最终值是通过将该初始值与对象其他部分的哈希码相结合,乘以小素数(即31)的幂来计算的。在这种情况下,使用id而不是任意素数绝对是正确的。

一般来说,哈希码是素数没有优势(它是需要素数的哈希桶的数量)。使用 aint作为它自己的哈希码(在你的情况下,就是idand numOfPages)是一种有效的方法。

于 2013-09-23T17:36:07.807 回答
2

它有助于了解 hashCode 的用途。它应该可以帮助您将理论上无限的对象集映射到适合少量“bin”,每个 bin 都有一个数字,每个对象根据其 hashCode 说明它想要进入哪个 bin。问题不在于是否可以做一件事或另一件事,而是您想做的事情是否与 hashCode 函数的用途相匹配。

根据http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode(),这与您返回的数字无关,而与它对不同对象的行为方式有关同班。

  1. 如果对象没有改变,则每次调用函数时 hashCode必须是相同的值。hashCode()
  2. 根据 , 相等的两个对象.equals必须具有相同的 hashCode。
  3. 不相等的两个对象可能具有相同的 hashCode。(如果不是这种情况,则根本没有使用 hashCode 的意义,因为每个对象都已经有一个唯一的对象指针)

如果您要重新实现 hashCode 函数,最重要的是要么依靠工具为您生成它,要么使用您理解的遵守这些规则的代码。基本的 JavahashCode函数使用了经过深入研究的、看似简单的字符串散列代码,因此您看到的代码是基于将所有内容都转换为字符串并回退到那个基础上的。

如果您不知道为什么会这样,请不要触摸它。只需依靠它工作并继续前进。31 非常重要,并确保了均匀的散列分布。请参阅为什么 Java 的 String 中的 hashCode() 使用 31 作为乘数?对于那个为什么。

但是,这也可能超出您的需要。你可以使用id,但是你基本上否定了使用 hashCode 的原因(因为现在每个对象都希望自己在一个 bin 中,将任何散列集合变成一个平面数组。有点傻)。

如果你知道你的 id 值的分布,就有更简单的 hashCodes 可以想出。假设您知道它们始终介于 0 和 Interger.MAX_VALUE 之间,并且您知道 id 之间永远不会有任何间隙,您可以简单地生成一个 hashCode,如

final int modulus = Intereger.MAX_VALUE / 255;
int hashCode() {
  return this.id % modulus;
}

现在,您有一个针对 255 个 bin 优化的 hashCode,满足了可接受的 hashCode 函数的必要要求。

于 2013-09-23T17:37:53.623 回答
0

注意:在我的回答中,我假设您知道如何使用哈希码。下面只讨论使用非零常数作为result可能产生的初始值的任何潜在优化。

如果id很少为 0,则可以使用它。但是,如果它经常为 0,您应该使用一些常量(只使用 1 应该没问题)。您希望它为非零的原因是该31 * result部分始终为散列添加一些值。那样的话,如果对象A的所有字段都为null 或 0,除了哈希码之外yearOfPublication = 1,对象的所有字段都为 null 或 0将是:BnumOfPages = 1

A.hashCode() => initialValue * 31 ^ 4 + 1
B.hashCode() => initialValue * 31 ^ 5 + 1

如您所见,如果initialValue为 0,则两个哈希码相同,但是如果不为 0,则它们将不同。它们最好是不同的,以减少使用哈希码的数据结构中的冲突,例如HashMap.

也就是说,在您的Book类示例中,它可能id永远不会为 0。事实上,如果id唯一标识 ,Book那么您可以让hashCode()方法只返回id.

于 2013-09-23T17:42:32.197 回答