为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

烙印99 2019-07-02 11:14:25
可变的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)?这是安全的还是明智的?还是行为依赖于语言?
查看完整描述

3 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

许多备受尊敬的开发人员,如BrianGoetz和Josh Bloch都注意到:

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


查看完整回答
反对 回复 2019-07-02
?
莫回无

TA贡献1865条经验 获得超7个赞

这既不安全,也不可取。无法检索由key 1映射到的值。在进行检索时,大多数散列映射将执行以下操作

Object get(Object key) {
    int hash = key.hashCode();
    //simplified, ignores hash collisions,
    Entry entry = getEntry(hash);
    if(entry != null && entry.getKey().equals(key)) {
        return entry.getValue();
    }
    return null;
}

在本例中,key1.hashcode()现在指向哈希表的错误桶,您将无法使用key 1检索value 1。

如果你做了这样的事,

Key key1 = new Key(0, 0);
map.put(key1, value1);
key1.setA(5);
Key key2 = new Key(0, 0);
map.get(key2);

这也不会检索值1,因为key 1和key 2不再相等,所以这个检查

    if(entry != null && entry.getKey().equals(key))

都会失败。


查看完整回答
反对 回复 2019-07-02
?
RISEBY

TA贡献1856条经验 获得超5个赞

哈希映射使用哈希代码和等式比较来识别特定的键值对和给定的键。如果HASMAP将键保留为对可变对象的引用,那么它将在同一个实例用于检索值的情况下工作。然而,考虑以下情况:

T keyOne = ...;
T keyTwo = ...;

// At this point keyOne and keyTwo are different instances and 
// keyOne.equals(keyTwo) is true.

HashMap myMap = new HashMap();

myMap.push(keyOne, "Hello");

String s1 = (String) myMap.get(keyOne); // s1 is "Hello"
String s2 = (String) myMap.get(keyTwo); // s2 is "Hello" 
                                        // because keyOne equals keyTwo

mutate(keyOne);

s1 = myMap.get(keyOne); // returns "Hello"
s2 = myMap.get(keyTwo); // not found

如果将密钥存储为引用,则上述内容为真。在Java中,情况通常是这样的。例如,在.NET中,如果键是值类型(始终由值传递),则结果将有所不同:

T keyOne = ...;
T keyTwo = ...;

// At this point keyOne and keyTwo are different instances 
// and keyOne.equals(keyTwo) is true.

Dictionary myMap = new Dictionary();

myMap.Add(keyOne, "Hello");

String s1 = (String) myMap[keyOne]; // s1 is "Hello"
String s2 = (String) myMap[keyTwo]; // s2 is "Hello"
                                    // because keyOne equals keyTwo

mutate(keyOne);

s1 = myMap[keyOne]; // not found
s2 = myMap[keyTwo]; // returns "Hello"

其他技术可能有其他不同的行为。然而,几乎所有这些都会出现这样一种情况,即使用可变键的结果不是确定性的,这在应用程序中是非常糟糕的情况-很难调试,甚至更难理解。


查看完整回答
反对 回复 2019-07-02
  • 3 回答
  • 0 关注
  • 438 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信