Java HashMap重写hashCode()和equals()方法后没有返回数据

6

最近我一直在使用Java的HashMap,但遇到了一些有趣的行为。我目前使用它来存储具有多个字段的键/值对象。为此,我已经重写了hashCode()和equals()方法,如下所示:

public final class TransitionState {

private String mStackSymbol;
private String mTransitionSymbol;
private int mState;

private static final int HASH_SEED = 7;     //Should be prime
private static final int HASH_OFFSET = 31;

//Constructor and getter methods here

public boolean equals(TransitionState other) {

    //Check that we aren't comparing against ourself
    if (this == other) {
        return true;
    }

    //Check that we are not comparing against null
    if (other == null) {
        return false;
    }

    //Check fields match
    if ((mState == other.getState()) &&
        (mTransitionSymbol.equals(other.getTransitionSymbol())) &&
        (mStackSymbol.equals(other.getStackSymbol()))) {

        return true;

    } else {
        return false;

    }
}

public int hashCode() {
    int intHash = HASH_SEED;

    //Sum hash codes for individual fields for final hash code
    intHash = (intHash * HASH_OFFSET) + mState;
    intHash = (intHash * HASH_OFFSET) + (mTransitionSymbol == null ? 0 : mTransitionSymbol.hashCode());
    intHash = (intHash * HASH_OFFSET) + (mStackSymbol == null ? 0 : mStackSymbol.hashCode());

    return intHash;
}
}

现在,我可以毫无问题地将项目放入Map中。然而,检索它们是另一回事。每当我尝试从HashMap中获取()时,就会返回NULL。我编写了一些测试代码来迭代Map并打印值,这就是事情变得混乱的地方,因为我的键对象的hashCode()与我在Map中拥有的hashCode()匹配,并且与已知值的相等性返回true。示例输出如下(请参见表格底部第四个转换):

Transition Table:
State  Symbol  Stack  Move
--------------------------
1, a, b, (1, pop) with key hashcode 212603 and value hashcode 117943
0, b, a, (0, pop) with key hashcode 211672 and value hashcode 117912
1, b, z, (1, push) with key hashcode 212658 and value hashcode 3459456
0, a, b, (0, pop) with key hashcode 211642 and value hashcode 117912
1, a, z, (0, push) with key hashcode 212627 and value hashcode 3459425
0, a, a, (0, push) with key hashcode 211641 and value hashcode 3459425
0, a, z, (0, push) with key hashcode 211666 and value hashcode 3459425
0, b, z, (1, push) with key hashcode 211697 and value hashcode 3459456
1, b, a, (1, pop) with key hashcode 212633 and value hashcode 117943
1, b, b, (1, push) with key hashcode 212634 and value hashcode 3459456

ababba

Transition from (0, a, z) with hashcode 211666
transition.equals(new TransitionState(0, "a", "z")) = true
HashMap containsKey() = false
Transition not found
false

你可以看到,这个关键字与哈希码匹配映射表中的条目,但是我被告知该条目不存在。 我尝试调试HashMap的containsKey()方法,它执行了一个检查NULL的get()方法。 进入get()后,发现循环只运行了一次就返回了NULL。

那么,这是HashMap的问题(可能不太可能)还是我可能做错了什么(更有可能)? 预先感谢您的帮助。

2个回答

19

你没有正确地覆盖 equals 方法...你需要这样做:

 public boolean equals(Object other)

目前你只是在重载它。

基本上,真正的覆盖仍然可以像现有的那样完成相同的工作,但要先进行测试:

if (!(other instanceof TransitionState))
{
    return false;
}
TransitionState otherState = (TransitionState) other;
// Now do the rest of the comparison

注意,在这种情况下,您不需要检查 null,因为它会导致 instanceof 测试失败。

现在的 Java 允许您添加注释,告诉编译器您确实正在尝试覆盖父方法:

 @Overrides
 public boolean equals(Object other)

现在编译器会提示您是否在名称拼写上出错或者函数签名错误。


2
为什么应该始终使用@Overrides的完美理由。 - matt b
@matt b:我正要添加那个 :) - Jon Skeet
1
老天,你是正确的。我太专注于实现了,结果最简单的事情也让我出错了。现在一切都正常了,非常感谢您及时的回复。 - MysteryMoose
我无法表达我有多开心能够找到这篇文章。我正在为教育目的编写一个DFA,但没有覆盖equals()方法,过去一小时我一直坐在那里感到沮丧,不知道为什么它不能工作。现在它终于可以工作了...它可以工作了...我太高兴了,它可以工作了!!! - Alex

2

Jon是正确的。此外,您应该执行以下操作:

将实例变量设置为final:

private final String mStackSymbol;
private final String mTransitionSymbol;
private final int mState;

如果您不能做到这一点,请确保您在将项目放入地图后不更改这些变量的值。如果状态在将它们放入地图之后更改,您将无法再次获取它们(至少无法通过其原始值获取)。

我曾经考虑过这个问题,但最终选择设计整个类使其无法被修改(没有设置器,所有成员都是私有的等)。尽管如此,我怀疑这会让类的设计更容易被后来的人理解。 - MysteryMoose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接