【问题标题】:Multiple key on HashMap: it deletes existing values?HashMap 上的多个键:它删除现有值?
【发布时间】:2014-09-27 17:10:45
【问题描述】:

我已经实现了我的多键类如下:

public class ProbabilityIndex {

    private int trueLabel;
    private int classifiedLabel;
    private int classifierIndex;

    public ProbabilityIndex(int trueLabel, int classifiedLabel, int classifierIndex) {
        this.trueLabel = trueLabel;
        this.classifiedLabel = classifiedLabel;
        this.classifierIndex = classifierIndex;
    }

    @Override
    public boolean equals(Object obj) {
        if ( !obj instanceof ProbabilityIndex)
            return false;
        if (obj == this)
            return true;

        ProbabilityIndex rhs = (ProbabilityIndex) obj;
        return new EqualsBuilder().
            append(trueLabel, rhs.trueLabel).
            append(classifiedLabel, rhs.classifiedLabel).
            append(classifierIndex, rhs.classifierIndex).
            isEquals();

    }

    @Override
    public int hashCode() {
        int hashCode = new HashCodeBuilder(17, 31).
                append(trueLabel).
                append(classifiedLabel).
                append(classifierIndex).
                toHashCode();
        return hashCode;
    }
}

请注意,trueLabelclassifiedLabelclassifierIndex 都是 0 或 1。

然后,我按如下方式使用我的密钥:

ProbabilityIndex key = new ProbabilityIndex(trueLabel, classifiedLabel, classifierIndex);
probabilities.put(key, new Double(value));

其中probabilities 声明如下:

HashMap<ProbabilityIndex, Double> probabilities;

但是trueLabelclassifiedLabelclassifierIndex的不同组合将元组写入probabilities中的相同位置,从而覆盖现有元组。

我该如何解决这个问题?

最小测试用例:

    HashMap<ProbabilityIndex, Double> map = new HashMap<ProbabilityIndex, Double>();
    map.put(new ProbabilityIndex(0, 0, 0), new Double(0.1));
    map.put(new ProbabilityIndex(0, 0, 1), new Double(0.2));
    map.put(new ProbabilityIndex(0, 1, 0), new Double(0.1));
    map.put(new ProbabilityIndex(0, 1, 1), new Double(0.2));
    map.put(new ProbabilityIndex(1, 0, 0), new Double(0.1));

这会插入 4 个元组而不是 5 个。

【问题讨论】:

  • 你能构造一个minimal test-case 来证明这一点吗?
  • 什么是 EqualsBuilder?
  • 添加了演示
  • 我也测试过。它工作正常。我得到以下输出 {ProbabilityIndex@7ba6e=0.1, ProbabilityIndex@7be10=0.1, ProbabilityIndex@7ba6f=0.2, ProbabilityIndex@7ba4f=0.1, ProbabilityIndex@7ba50=0.2}

标签: java hashmap key


【解决方案1】:

我只能告诉你,哈希表永远不会覆盖具有相同哈希码的对象(哈希冲突);它只会降低检索效率。

错误地覆盖您的条目的唯一方法是为键提供equals 方法,该方法为不同的键返回true

一些与您的问题没有直接关系的进一步建议:如果您只有三个二态变量,那么该类的完整值集的基数仅为 8。而不是您使用的复杂哈希码生成器,您可以只用三位构建哈希码,每个位代表一个变量。这显然可以确保对象的每个状态都有不同的哈希码。

我已经使用hashCode()equals() 的以下实现验证了您的代码(我不得不更改equals 以使您的示例真正独立):

@Override public boolean equals(Object obj) {
  if (!(obj instanceof ProbabilityIndex)) return false;
  if (obj == this) return true;
  ProbabilityIndex rhs = (ProbabilityIndex) obj;
  return this.trueLabel == rhs.trueLabel
      && this.classifiedLabel ==  rhs.classifiedLabel
      && this.classifierIndex == rhs.classifierIndex;
}

@Override public int hashCode() {
  return trueLabel | (classifiedLabel << 1) | (classifierIndex << 2);
}

您的测试代码生成了一个包含五个条目的映射。

最后一点,如果哈希表的最大大小仅为 8,您甚至不需要哈希表。由上述哈希码索引的大小为 8 的普通数组就足够了。

【讨论】:

  • 您建议的哈希码解决了问题。所以,我猜我的函数正在生成重合哈希码。
  • 不,正如我在答案中解释的那样,哈希码冲突无法重现您的问题。要说服自己,请尝试使用 public int hashCode() { return 1; } 的代码。
  • 那么问题出在哪里,因为 equals 方法的代码在两个代码中都是相同的。以前的代码使用的是通用语言 API,而您使用的是直接代码。但是两个代码是一样的。
  • @Hansraj 好问题... 必须 OP 的equals 出了点问题,但没有代码来重现它,没人知道。可能OP用错了EqualsBuilder,我没去查。
猜你喜欢
  • 2012-05-17
  • 2021-01-28
  • 1970-01-01
  • 2011-06-24
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多