【问题标题】:Java Hashtable overwrites existing key with new key during 'put'Java Hashtable 在“放置”期间用新密钥覆盖现有密钥
【发布时间】:2011-11-30 07:03:35
【问题描述】:

我正在尝试使用 Eclipse 将条目添加到 Java 中的哈希表中。在 put 操作期间,只有一个键被新的键和值覆盖。哈希表的计数得到了正确维护,但是(键,值)对中的一个丢失了。

这是我的示例代码:

ArrayList<Double> list;
Hashtable<Val,ArrayList<Double>> numbers = new Hashtable<Val,ArrayList<Double>>();

while((line = brMyHashval.readLine()) != null)
{
    if(!(line.isEmpty()))
    {               
        String[] temp;
        temp = line.split(" ");      
        eDouble = Double.parseDouble(temp[2].toString());

        Val key = new Val(Double.parseDouble(temp[0].toString()) ,Double.parseDouble(temp[1].toString()) );

        if(!(numbers.containsKey(key)))
        {
            list = new ArrayList<Double>();
            numbers.put(key, list);

        }
        else
        {
            list = numbers.get(key);
        }
        list.add(eDouble); 
     }
}

我已经习惯在 eclipse 中内置 'hashcode' 和 'equals' 方法来比较类对象。

输入文本文件:

1.0 2.0 9.0
3.0 4.0 9.0
5.0 6.0 9.0
1.0 2.0 8.0
5.0 6.0 8.0
1.0 2.0 7.0
**7.0 8.0 7.0** // After this point a new hash entry gets added for key(7,8), But key (1,2) get deleted from the hashtable, though count gets increased to 4.
3.0 4.0 7.0
5.0 6.0 10.0
1.0 2.0 10.0
1.0 3.0 10.0
1.0 4.0 10.0

为什么密钥会在特定时刻被删除?

[edit] hashcode和equals:我用eclipse自动导入这些方法 // (x,y) 是 (a,b)

  class Val

{
    double x;
    double y;

Val(double X, double Y)
{
    x = X;
    y = Y;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(x);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(y);
    result = prime * result + (int) (temp ^ (temp >>> 32));
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Val other = (Val) obj;
    if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
        return false;
    if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
        return false;
    return true;
}

}

【问题讨论】:

  • 你的 hashCode 和 equals 是做什么的?
  • 什么是“Val”类?和“K”一样吗?
  • 是的。对不起。 Val 类是 K 类
  • 我无法重现,它按照您描述的方式工作,您希望它在这里工作,所有密钥都创建一次。你能发布一个SSCCE吗?您同时使用 K 类和 Val 类,我认为您显示的代码不是您运行的代码。
  • 问题不在于调试器,而在于数据如何存储在 HashMap 中——看我的回答

标签: java hashtable


【解决方案1】:

问题是您正在使用调试器检查 HashMap 的内容。

我假设键 (1,2) 和 (7,8) 都保存在用于保存键的 HashTable 的同一个槽中。添加 (7,8) 后,(1,2) 被移到 (7,8) 的“后面” - 您必须检查 (7,8) 条目的 next 条目。

将以下内容添加到代码末尾以查看 HashMap 中的真正内容:

    for (Val key : numbers.keySet()) {
        System.out.printf("%.1f %.1f: %s%n", key.x, key.y, numbers.get(key));
    }

【讨论】:

  • 使用调试器检查 HashMap 可能不直观。
【解决方案2】:

上面的 Sumindra 意味着你想使用自定义类作为 Map 中的键,你必须按照指定的方式编写 equals() 和 hashCode() 方法。执行以下操作(例如:

public boolean equals(K other) {
    return a == other.a && b == other.b;
}

public int hashCode() {
    return new Double(a).hashCode() ^ new Double(b).hashCode();
}

这保证:

  • 如果两个 K 对象具有相同的成员,则返回相等
  • 如果两个 K 对象具有相同的成员,则它们具有相同的 hashCode

这是 Map 键对象的要求。

【讨论】:

  • 即使使用您提到的代码,也会发生同样的问题。即两个 K 对象对于不同的成员返回相等。我应该以不同的方式编写方法吗?即使使用^,我也找不到任何理由为不同的成员提供相同的值
  • @SyncMaster 注意两个对象可以返回相同的hashCode,但仍然不同!例如,字符串可以比散列码有更多的位,不可能为每个字符串提供独特的代码。在您的情况下,两个双精度数的位数多于 long (hashCode 返回类型)
  • @CarlosHeuberger :所以处理这种情况的更好方法是编写我自己的适当哈希码?
  • @SyncMaster - 没有必要也没有办法拥有唯一的哈希码。在您的情况下,密钥由 2 个双精度定义,即 2 乘以 64 位,您至少需要 128 位才能拥有唯一的哈希码。在 Java 中,哈希码只有一个长的 32 位,无法唯一表示 128 位!
【解决方案3】:

我无法重现您的问题,这是我正在运行的确切代码(未简化为其他答案,以使其尽可能接近您的原始问题)。

public class HashProblem {

    public static class Val {
        private double x;
        private double y;

        public Val(double x, double y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            long temp;
            temp = Double.doubleToLongBits(x);
            result = prime * result + (int) (temp ^ (temp >>> 32));
            temp = Double.doubleToLongBits(y);
            result = prime * result + (int) (temp ^ (temp >>> 32));
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Val other = (Val) obj;
            if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
                return false;
            if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
                return false;
            return true;
        }
    }

    public static void main(String... args) throws Exception {
        ArrayList<Double> list;
        String line;
        BufferedReader brMyHashval = new BufferedReader(new InputStreamReader(new FileInputStream("HashProblem.txt")));
        Hashtable<Val, ArrayList<Double>> numbers = new Hashtable<Val, ArrayList<Double>>();

        while ((line = brMyHashval.readLine()) != null) {
            if (!(line.isEmpty())) {
                String[] temp;
                temp = line.split(" ");
                Double eDouble = Double.parseDouble(temp[2].toString());

                Val key = new Val(Double.parseDouble(temp[0].toString()), Double.parseDouble(temp[1].toString()));

                if (!(numbers.containsKey(key))) {
                    list = new ArrayList<Double>();
                    numbers.put(key, list);
                    System.err.println("Created " + key.x + " " + key.y);
                } else {
                    list = numbers.get(key);
                }
                list.add(eDouble);
                System.err.println("Inserted into " + key.x + " " + key.y + " value " + eDouble + " size " + list.size() + " " + list);
            }
        }
    }

我从日志中得到的输出是

Created 1.0 2.0
Inserted into 1.0 2.0 value 9.0 size 1 [9.0] 
Created 3.0 4.0
Inserted into 3.0 4.0 value 9.0 size 1 [9.0]
Created 5.0 6.0
Inserted into 5.0 6.0 value 9.0 size 1 [9.0]
Inserted into 1.0 2.0 value 8.0 size 2 [9.0, 8.0]
Inserted into 5.0 6.0 value 8.0 size 2 [9.0, 8.0]
Inserted into 1.0 2.0 value 7.0 size 3 [9.0, 8.0, 7.0]
Created 7.0 8.0
Inserted into 7.0 8.0 value 7.0 size 1 [7.0]
Inserted into 3.0 4.0 value 7.0 size 2 [9.0, 7.0]
Inserted into 5.0 6.0 value 10.0 size 3 [9.0, 8.0, 10.0]
Inserted into 1.0 2.0 value 10.0 size 4 [9.0, 8.0, 7.0, 10.0]
Created 1.0 3.0
Inserted into 1.0 3.0 value 10.0 size 1 [10.0]
Created 1.0 4.0
Inserted into 1.0 4.0 value 10.0 size 1 [10.0]

这不是你所期望的吗?

其他答案对简化 hashCode 和 equals 有好处。此外,您不需要对已经是字符串的对象执行 toString()。

【讨论】:

    【解决方案4】:

    确保 hash 和 equals 符合他们的要求。

    每个实例都应该有一个唯一的哈希值,如果它们相等,则等于应该为真。误报意味着误报值映射到相同的键。 See this link.

    【讨论】:

      猜你喜欢
      • 2021-06-23
      • 2023-03-23
      • 1970-01-01
      • 2020-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多