【问题标题】:HashMap ignoring overridden hashCode and equals methodsHashMap 忽略覆盖的 hashCode 和 equals 方法
【发布时间】:2020-02-09 02:37:40
【问题描述】:

我正在从一个文件加载网络流量数据。我正在加载的信息是攻击者 IP 地址、受害者 IP 地址和日期。我已将这些数据合并到一个 Traffic 对象中,为此我定义了 hashCodeequals 函数。尽管如此,我将它们加载到的HashMap 将相同的Traffic 对象视为不同的键。整个 Traffic 对象以及 main 方法中的一些简单测试代码如下:

import java.util.HashMap;

public class Traffic {

    public String attacker;
    public String victim;
    public int date;

    //constructors, getters and setters

    @Override
    public int hashCode() {
        long attackerHash = 1;
        for (char c:attacker.toCharArray()) {
            attackerHash = attackerHash * Character.getNumericValue(c) + 17;
        }

        long victimHash = 1;
        for (char c:victim.toCharArray()) {
            victimHash = victimHash * Character.getNumericValue(c) + 17;
        }

        int IPHash = (int)(attackerHash*victimHash % Integer.MAX_VALUE);
        return (IPHash + 7)*(date + 37) + 17;
    }

    public boolean equals(Traffic t) {
        return this.attacker.equals(t.getAttacker()) && this.victim.equals(t.getVictim()) && this.date == t.getDate();
    }

    public static void main(String[] args) {
        Traffic a = new Traffic("209.167.099.071", "172.016.112.100", 7);
        Traffic b = new Traffic("209.167.099.071", "172.016.112.100", 7);
        System.out.println(a.hashCode());
        System.out.println(b.hashCode());

        HashMap<Traffic, Integer> h = new HashMap<Traffic, Integer>();
        h.put(a, new Integer(1));
        h.put(b, new Integer(2));
        System.out.println(h);
    }
}

我不能说我的哈希方法的强度,但前两个打印的输出是相同的,这意味着它至少适用于这种情况。

由于 a 和 b 的数据相同(因此 equals 返回 true),并且哈希值相同,HashMap 应该将它们识别为相同并将值从 1 更新为 2 而不是创建第二个值为 2 的条目。不幸的是,它不会将它们识别为相同的,最终打印的输出如下:

{packagename.Traffic@1c051=1, packagename.Traffic@1c051=2}

我对此的最佳猜测是HashMap 的内部工作忽略了我自定义的hashCodeequals 方法,但如果是这样,那为什么呢?如果这个猜测是错误的,那么这里发生了什么?

【问题讨论】:

  • @Override 注释的存在是有充分理由的。用它。 (并且不要重新发明 String 的哈希码,只需调用它即可。)(并且“如果它们的哈希码匹配,则两个对象是相同的”是定义相等性的糟糕方式。)
  • 你有没有试过打电话给a.equals(b)看看是不是true
  • @OrestSavchak 在这种情况下可能会提供误报。
  • @Ecko 你在hashCode() 上使用了@Override,而不是equals(Traffic)。我怀疑 chrylis 指的是您没有始终如一地使用它。
  • @Ecko “此外,equals 确实返回 true,我正在努力看看这将如何成为误报”您调用的方法与 HashMap 不同。尝试调用a.equals((Object) b)

标签: java hash hashmap hashcode


【解决方案1】:

这里的问题是您的equals 方法,它不会覆盖Object#equals。为了证明这一点,以下代码将使用@Override 注解编译:

@Override
public boolean equals(Traffic t) {
    return this.attacker.equals(t.getAttacker()) && 
        this.victim.equals(t.getVictim()) && 
        this.date == t.getDate();
}

HashMap 的实现使用Object#equals 并且不是您的自定义实现。您的 equals 方法应该接受 Object 作为参数:

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

    Traffic t = (Traffic) o;

    return Objects.equals(attacker, t.attacker) &&
        Objects.equals(victim, t.victim) &&
        date == t.date;
}

【讨论】:

  • 谢谢,这很好地解决了它。 equals 方法的错误定义是我不小心复制到问题中的旧占位符代码;我编辑了问题以反映正确的代码。我曾尝试添加一个@Override 注释,但由于我错误地使用Traffic 作为我的参数而不是Object,它引发了一个错误,我将其删除。
  • @Ecko 我刚刚注意到并编辑了答案以适应它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多