突变导致错误的结果
在项目中,我使用的HashMap为了存储一些数据,以及最近我发现,当我的突变HashMap的键上,一些意想不到的错误的结果可能发生。例如:突变导致错误的结果
HashMap<ArrayList,Integer> a = new HashMap<>();
ArrayList list1 = new ArrayList<>();
a.put(list1, 1);
System.out.println(a.containsKey(new ArrayList<>())); // true
list1.add(5);
ArrayList list2 = new ArrayList<>();
list2.add(5);
System.out.println(a.containsKey(list2)); // false
注意两个a.keySet().iterator().next().hashCode() == list2.hashCode()
和a.keySet().iterator().next().equals(list2)
是真实的。
我不明白为什么会发生,指的是事实,两个对象是相等的,并具有相同的哈希码。有谁知道这是什么原因,并且如果有其他类似的结构可以让钥匙变形?谢谢。
可变键总是一个问题。如果突变可能改变它们的散列码和/或equals()
的结果,则键被认为是可变的。也就是说,列表通常会生成他们的哈希码并根据它们的元素检查相等性,所以它们几乎从来都不是地图关键字的好选择。
你的例子有什么问题?当添加密钥时,它是一个空列表,从而产生与包含元素时不同的哈希码。因此,即使密钥和list2
的哈希码与更改密钥列表后相同,您也不会找到该元素。为什么?只是因为地图看起来不对劲。
实施例(简化):
让我们先从一些假设:
- 空列表,如果该列表包含它返回哈希码元件5返回的0
- 的哈希码5
- 我们的地图具有16个桶
- 铲斗索引由散列码%16(我们的桶的数量) 确定(默认)
如果你现在它被插入到桶0空单增加,由于其哈希码。
当你做list1
查找它看起来斗5由于5哈希码自那桶是空的什么都不会被发现。
的问题是,你的密钥列表改变其哈希码,因此应该被置于不同的水桶,但地图并不知道发生这种情况(以及这样做可能会导致一堆其他问题)。
非常感谢,我现在可以理解它。 – RanSch
这是因为HashMap使用hashCode()
对象方法结合equals(Object obj)
来检查此映射是否包含对象。
参见:
ArrayList<Integer> a = new ArrayList<>();
a.add(1);
System.out.println(a.hashCode());
a.add(2);
System.out.println(a.hashCode());
这个例子显示,你的ArrayList的hashCode发生了变化。
根据的Javadoc Map:
注意:如果使用可变对象作为地图 按键很大,一定要小心。如果对象 的值以影响等于比较的方式进行更改,而 对象是地图中的关键字,则未指定地图的行为。这种禁令的一个特例是 地图不允许自己作为关键字。尽管 对于包含自身值的地图是允许的,但建议您谨慎使用 :在这样的地图上,不再定义equals和hashCode方法 。
你的清单是钥匙,你正在改变它们。如果列表中的内容不是,那么这将不会成为问题什么决定哈希码的值和什么是平等的,但这不是你的情况。如果你仔细想想,改变地图的关键是没有什么意义的。关键在于识别价值,如果该密钥更改,所有投注都关闭。
该映射在插入时插入给定散列码的值。当您稍后搜索它时,它会使用参数的哈希码来确定它是否是命中。我想你会发现,如果你插入的list1
值已经插入,你会看到“true”被打印出来,因为list2.hashCode()
在插入时会产生与list1
相同的哈希码。
你不应该在你的散列表中使用一个可变对象作为键。
那么,当你把list1作为第三行中的键时,基本上会发生什么,就是map计算它的hashCode,它将在稍后在containsKey(someKey)中进行比较。 但是当u突变第5行中的list1时,其hashCode实质上发生了改变。 所以如果u现在要做的
System.out.println(a.containsKey(list1));
5号线后,会说false
和如果u做System.out.println(a.get(list1));
它会说null
作为其比较两个不同的散列码
不要使用可变值hashmap键。我认为它在HashMap的文档中说过,但目前我找不到它。 –
我知道我不应该(关于我已经展示的例子),但我不明白原因(对象是平等的,并具有相同的哈希代码),我正在寻找一个类似的结构,允许。 – RanSch
“......如果有其他类似的结构可以让钥匙变形?” - 钥匙(至少是形成钥匙的部件)从来不会变得可变,因为那样会违反它们的使用。从现实世界的角度来看它:如果你改变你的钥匙,它不会再打开锁。如果你改变锁定,你需要一个新的密钥。 – Thomas