可变的hashmap键是一种危险的做法吗?

可变的hashmap键是一种危险的做法吗?

使用可变对象作为Hashmap键是不好的做法吗?当您试图使用经过修改以更改其哈希代码的键从Hashmap检索值时,会发生什么情况?

例如,给定

class Key
{
    int a; //mutable field
    int b; //mutable field

    public int hashcode()
        return foo(a, b);
    // setters setA and setB omitted for brevity
}

有代码

HashMap<Key, Value> map = new HashMap<Key, Value>();

Key key1 = new Key(0, 0);
map.put(key1, value1); // value1 is an instance of Value

key1.setA(5);
key1.setB(10);

如果我们现在打电话给map.get(key1)?这是安全的还是明智的?还是行为依赖于语言?


烙印99
浏览 442回答 3
3回答

扬帆大鱼

许多备受尊敬的开发人员,如BrianGoetz和Josh Bloch都注意到:如果一个对象的hashCode()值可以根据其状态进行更改,那么我们在使用这些对象作为基于哈希的集合中的键时必须小心,以确保当它们被用作散列键时,我们不允许它们的状态更改。所有基于散列的集合假设对象的散列值在用作集合中的键时不会更改。如果键的哈希代码在集合中发生更改,则可能会出现一些不可预测和令人困惑的结果。在实践中,这通常不是一个问题-使用像List这样的可变对象作为HashMap中的键是不常见的。

莫回无

这既不安全,也不可取。无法检索由key 1映射到的值。在进行检索时,大多数散列映射将执行以下操作Object&nbsp;get(Object&nbsp;key)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;key.hashCode(); &nbsp;&nbsp;&nbsp;&nbsp;//simplified,&nbsp;ignores&nbsp;hash&nbsp;collisions, &nbsp;&nbsp;&nbsp;&nbsp;Entry&nbsp;entry&nbsp;=&nbsp;getEntry(hash); &nbsp;&nbsp;&nbsp;&nbsp;if(entry&nbsp;!=&nbsp;null&nbsp;&&&nbsp;entry.getKey().equals(key))&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;entry.getValue(); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null; }在本例中,key1.hashcode()现在指向哈希表的错误桶,您将无法使用key 1检索value 1。如果你做了这样的事,Key&nbsp;key1&nbsp;=&nbsp;new&nbsp;Key(0,&nbsp;0); map.put(key1,&nbsp;value1); key1.setA(5); Key&nbsp;key2&nbsp;=&nbsp;new&nbsp;Key(0,&nbsp;0); map.get(key2);这也不会检索值1,因为key 1和key 2不再相等,所以这个检查&nbsp;&nbsp;&nbsp;&nbsp;if(entry&nbsp;!=&nbsp;null&nbsp;&&&nbsp;entry.getKey().equals(key))都会失败。

RISEBY

哈希映射使用哈希代码和等式比较来识别特定的键值对和给定的键。如果HASMAP将键保留为对可变对象的引用,那么它将在同一个实例用于检索值的情况下工作。然而,考虑以下情况:T&nbsp;keyOne&nbsp;=&nbsp;...; T&nbsp;keyTwo&nbsp;=&nbsp;...; //&nbsp;At&nbsp;this&nbsp;point&nbsp;keyOne&nbsp;and&nbsp;keyTwo&nbsp;are&nbsp;different&nbsp;instances&nbsp;and&nbsp; //&nbsp;keyOne.equals(keyTwo)&nbsp;is&nbsp;true. HashMap&nbsp;myMap&nbsp;=&nbsp;new&nbsp;HashMap(); myMap.push(keyOne,&nbsp;"Hello"); String&nbsp;s1&nbsp;=&nbsp;(String)&nbsp;myMap.get(keyOne);&nbsp;//&nbsp;s1&nbsp;is&nbsp;"Hello" String&nbsp;s2&nbsp;=&nbsp;(String)&nbsp;myMap.get(keyTwo);&nbsp;//&nbsp;s2&nbsp;is&nbsp;"Hello"&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;because&nbsp;keyOne&nbsp;equals&nbsp;keyTwo mutate(keyOne); s1&nbsp;=&nbsp;myMap.get(keyOne);&nbsp;//&nbsp;returns&nbsp;"Hello" s2&nbsp;=&nbsp;myMap.get(keyTwo);&nbsp;//&nbsp;not&nbsp;found如果将密钥存储为引用,则上述内容为真。在Java中,情况通常是这样的。例如,在.NET中,如果键是值类型(始终由值传递),则结果将有所不同:T&nbsp;keyOne&nbsp;=&nbsp;...; T&nbsp;keyTwo&nbsp;=&nbsp;...; //&nbsp;At&nbsp;this&nbsp;point&nbsp;keyOne&nbsp;and&nbsp;keyTwo&nbsp;are&nbsp;different&nbsp;instances&nbsp; //&nbsp;and&nbsp;keyOne.equals(keyTwo)&nbsp;is&nbsp;true. Dictionary&nbsp;myMap&nbsp;=&nbsp;new&nbsp;Dictionary(); myMap.Add(keyOne,&nbsp;"Hello"); String&nbsp;s1&nbsp;=&nbsp;(String)&nbsp;myMap[keyOne];&nbsp;//&nbsp;s1&nbsp;is&nbsp;"Hello" String&nbsp;s2&nbsp;=&nbsp;(String)&nbsp;myMap[keyTwo];&nbsp;//&nbsp;s2&nbsp;is&nbsp;"Hello" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;because&nbsp;keyOne&nbsp;equals&nbsp;keyTwo mutate(keyOne); s1&nbsp;=&nbsp;myMap[keyOne];&nbsp;//&nbsp;not&nbsp;found s2&nbsp;=&nbsp;myMap[keyTwo];&nbsp;//&nbsp;returns&nbsp;"Hello"其他技术可能有其他不同的行为。然而,几乎所有这些都会出现这样一种情况,即使用可变键的结果不是确定性的,这在应用程序中是非常糟糕的情况-很难调试,甚至更难理解。
打开App,查看更多内容
随时随地看视频慕课网APP