java - HashMap 中键的突变会导致错误的结果

标签 java hashmap

在我的项目中,我使用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) 为 true。

我无法理解为什么会发生这种情况,因为这两个对象相等并且具有相同的哈希码。有谁知道这是什么原因,以及是否有任何其他类似的结构允许 key 突变?谢谢。

最佳答案

可变键始终是一个问题。如果突变可以改变键的哈希码和/或equals()的结果,则键被认为是可变的。话虽这么说,列表通常会生成哈希码并根据其元素检查相等性,因此它们几乎永远不会成为映射键的良好候选者。

你的例子有什么问题?添加键时,它是一个空列表,因此会生成与包含元素时不同的哈希码。因此,即使在更改键列表后,键和 list2 的哈希码相同,您也找不到该元素。为什么?仅仅是因为 map 看起来在错误的存储桶中。

示例(简化):

让我们从一些假设开始:

  • 空列表返回哈希码 0
  • 如果列表包含元素 5,则返回哈希码 5
  • 我们的 map 有 16 个桶(默认)
  • 存储桶索引由哈希码 % 16(我们的存储桶数量)决定

如果您现在添加空列表,由于其哈希码,它会被插入到存储桶 0 中。

当您使用 list1 进行查找时,由于哈希码为 5,它将在存储桶 5 中查找。由于该存储桶为空,因此什么也找不到。

问题是您的 key 列表更改了其哈希码,因此应该放入不同的存储桶中,但 map 不知道应该发生这种情况(这样做可能会导致一堆其他问题)。

关于java - HashMap 中键的突变会导致错误的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45977296/

相关文章:

java - 重命名 hashmap 的键值而不是重新插入

ruby - Ruby 中的有序哈希有什么用?

java - 用于存储 2D 数组值的内存效率最高的数据结构 (Java)

java - 验证不适用于 persist

C++ 中的 Java 数组列表

java - 如何从列表适配器刷新布局/上下文(我认为这就是我需要的)

java - 改造 - HTTP 失败 : java. lang.IllegalStateException:当我尝试刷新 token 时已关闭

java - 使用Comparator.comparing(HashMap::get)作为比较器时发生意外行为

java - 将键值合并为另一个具有 java 中重复项的映射的新值

eclipse - 不同的 Java 开发人员和程序版本?