【问题标题】:Overriding hashCode with a class with two generics fields使用具有两个泛型字段的类覆盖 hashCode
【发布时间】:2019-03-04 17:25:46
【问题描述】:

我正在实现一个类Pair 以将其用作HashMap 的具有两个值的键。我使用泛型来保持字段变量的类型。我设法编写了代码的最大部分:

public class Pair<L, R>
{
    private L left;
    private R right;


    Pair(L left, R right)
    {
        this.left = left;
        this.right = right;
    }


    public L getLeft()
    {
        return left;
    }


    public R getRight()
    {
        return right;
    }


    public void setLeft(L left)
    {
        this.left = left;
    }


    public void setRight(R right)
    {
        this.right = right;
    }


    @Override
    public boolean equals(Object obj)
    {
        if (obj instanceof Pair< ? , ? >)
        {
            Pair< ? , ? > pair = (Pair< ? , ? >)obj;
            return left.equals(pair.getLeft()) && right.equals(pair.getRight());
        }
        return false;
    }

    @Override
    public String toString()
    {
        return "Pair " + Integer.toHexString(hashCode()) + ": (" + left.toString() + ", " + right.toString()
               + ")";
    }
}

我的问题是创建正确的hashCode 方法,它肯定为相同的对象提供相同的哈希码,为不同的对象提供不同的哈希码。一些提示?

【问题讨论】:

  • 相等的对象应该有相同的哈希码,但两个不相等的对象不一定有不同的哈希码(它们应该经常不同)。

标签: java hashmap hashcode keyvaluepair


【解决方案1】:

不要重新发明轮子。

只需使用return Objects.hash(left, right);

【讨论】:

  • 这正是我一直在寻找的!
  • 它对 java 1.7 真的很有帮助。但也许,您会注意到,我们应该为 Java 1.6 及更低版本选择什么?
  • @Puzzle:复制the implementation。它非常简单,适用于 1.6 甚至 1.5……
【解决方案2】:

您已经在依赖左右 equals 方法,那么为什么不也依赖他们的 hashcodes 呢?

@Override
public int hashCode()
{
    final int prime = 31;
    int result = 1;
    result = prime * result + (left ==null? 0 : left.hashCode());
    result = prime * result + (right ==null? 0 : right.hashCode());
    return result;
}

【讨论】:

  • 这就是您在使用return Objects.hash(left, right); 时免费获得的东西,至少 Oracle 的 Java 7 实现完全使用了它。但是,使用 JRE 方法可以在未来进行改进……
  • @Holger 是的,感谢您指出这一点,我不知道该方法(仍在 JDK 6 上)但是,看起来 Objects.hash() 会导致额外的数组创建,这可能不会在这种情况下很重要,但需要考虑。
  • 好建议,我也想过使用左右哈希码,但不知道如何最好地使用它们。无论如何,我会更好地坚持原生 java 实现。
【解决方案3】:

这应该可以解决问题(当然哈希码永远不会保证不同)

@Override
public int hashCode() {
    return (left.hashCode()+"/"+right.hashCode()).hashCode();
}

如果 left 或 right 可以为 null,则需要更多代码来处理。

【讨论】:

  • @Holger:是什么让您认为left.hashCode()-right.hashCode() 提供了更好的结果?不难想象left.hashCode()-right.hashCode() 是个坏主意的情况……例如,Integer 类将值返回为 hashCode;因此,如果您使用这个泛型类来创建 Integer 对,那么创建哈希冲突非常容易(并且在正常用例中很可能)。
  • String.hashCode 针对任意Strings 进行了优化,最好是人类可读的文本。它没有针对Strings 进行优化,最多使用 65536 个字符中的 12 个不同的字符。仅仅因为很难看到缺陷并不意味着它是一个更好的解决方案。您的解决方案只是隐藏正在发生的事情。
  • 我不认为 String.hashCode 对字符串的内容做出任何假设。见stackoverflow.com/questions/299304/…
  • 不是String.hashCode 的方法做出任何假设,而是用来证明结果足够的一组测试用例产生了影响。 “使用 31” 想法的最初发明者(我认为没有人知道谁是第一个)不需要知道为什么它有效。尽管如此,通过对String 表示的逐个字符进行散列来散列int使用为文本设计的算法,这只是由于“逐字符”性质,独立于使用的31号码。
  • String.hashCode 可能是整个 Java 世界中最重要的 hashCode() 实现。我相信它适用于中文文本,就像它适用于仅包含 '0' 和 '1' 的字符串一样,因为许多不同的程序都依赖它。因此,我的建议的不足之处可能是构建字符串所需的开销,但如果它是 String.hashCode(),我会被诅咒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 1970-01-01
相关资源
最近更新 更多