【问题标题】:How does Java HashMap store entries internallyJava HashMap 如何在内部存储条目
【发布时间】:2012-03-25 01:43:50
【问题描述】:

假设您有一个键类 (KeyClass),其中覆盖了 equals、hashCode 和 clone 方法。假设它有 2 个原始字段,一个 String(名称)和一个 int(id)。

现在你定义

KeyClass keyOriginal, keyCopy, keyClone;

keyOriginal = new KeyClass("original", 1);
keyCopy = new KeyClass("original", 1);
keyClone = KeyClass.clone();

现在

keyOriginal.hashCode() == keyCopy.hashCode() == keyClone.hashCode()
keyOriginal.equals(keyCopy) == true
keyCopy.equals(keyClone) == true

所以就 HashMap 而言,keyOriginal、keyCopy 和 keyClone 是无法区分的。

现在,如果你使用 keyOriginal 将条目放入 HashMap,你可以使用 keyCopy 或 keyClone 将其取回,即

map.put(keyOriginal, valueOriginal);
map.get(keyCopy) will return valueOriginal
map.get(keyClone) will return valueOriginal

此外,如果在将键放入映射后对其进行变异,则无法检索原始值。所以对于例如

keyOriginal.name = "mutated";
keyOriginal.id = 1000;

Now map.get(keyOriginal) will return null

所以我的问题是

当你说 map.keySet() 时,它会返回地图中的所有键。 HashMap 类如何知道映射中存储的键、值和条目的完整列表是什么?

编辑 据我了解,我认为它可以通过将 Entry 键作为最终变量来工作。

static class Entry<K,V> implements Map.Entry<K,V> { 
  final K key; 

(docjar.com/html/api/java/util/HashMap.java.html)。因此,即使我在将密钥放入地图后对其进行了变异,也会保留原始密钥。我的理解正确吗?但是即使保留了原始的键引用,仍然可以改变它的内容。那么如果内容发生了变异,而K,V仍然保存在原来的位置,那么检索是如何进行的呢?

编辑 如果您在放入哈希映射后更改密钥,检索将失败。因此,不建议您使用可变的 hashmap 键。

【问题讨论】:

  • 为什么不自己查看源代码,它是公开且易于访问的
  • 如果不是这样,它现在就不是一个数据结构了,不是吗?您认为它如何能够查找您想要的特定密钥?
  • The source 对于好奇的人。
  • 查找单个键只是计算hashCode然后使用equals比较每个碰撞的键的问题。但是 HashMap 需要知道其中存储的所有键是什么;没有客户端传递密钥。所以下面 Louis 的回答看起来是这样的。

标签: java hashmap


【解决方案1】:

HashMap 维护一个条目表,其中包含对关联键和值的引用,根据它们的哈希码进行组织。如果你改变了一个键,那么哈希码会改变,但是HashMap中的条目仍然按照原始的哈希码放在哈希表中。这就是map.get(keyOriginal) 将返回 null 的原因。

map.keySet() 只是遍历哈希表,返回它拥有的每个条目的键。

【讨论】:

  • 谢谢。 “如果你改变了一个键,那么哈希码会改变,但是HashMap中的条目仍然按照原始的哈希码放在哈希表中。”我阅读了源代码。据我了解,我认为它可以通过将 Entry 键作为最终变量来工作。 static class Entry implements Map.Entry { final K key; (docjar.com/html/api/java/util/HashMap.java.html)。因此,即使我在将密钥放入地图后对其进行了变异,也会保留原始密钥。我的理解正确吗?
  • 它持有一个对密钥的reference。如果您更改密钥,那么基本上,HashMapentrySet() 将看到修改后的密钥,但 get(key) 会在错误的位置查找,因此当您尝试 map.get(key) 时它会返回 null .基本上,在Map 中使用可变键只是自找麻烦,实际上继续在Map 中更改键就像在膝盖上开枪。
  • 再次感谢。但是我还没有理解的是这个。您拥有 keyOriginal 和 keyCopy ,其中包含相同的字段值。现在您将 keyOriginal 放入地图中。假设它进入内部哈希表条目表中的位置 100。现在你改变 keyOriginal。它仍然存储在位置 100。现在,如果您说 map.get(keyCopy),它确实会返回原始关联值。如果 keyOriginal.equals(keyCopy) 为假,它怎么能做到这一点?
  • 你测试过吗?如果这是真的,我会感到非常惊讶。我强烈希望map.get(keyCopy) 能够返回null
  • 是的,我已经测试过了。我将在一个新问题中添加测试代码。我不能在这里添加它,因为它太长了。
【解决方案2】:

如果您更改条目而不更改 hashCode,则您是安全的。因此,最佳做法是让 hashCode、equals 和 compareTo 中的所有字段都为 final 和不可变的。

【讨论】:

  • 谢谢。好资料。 -- “如果你改变了条目而不是 hashCode,你是安全的。” -- 我要补充一点,你仍然不安全,因为 equals 会失败。
  • 所以我想可以肯定地说,如果你在添加到 map 之后改变键(hashCode 不同,equals 为 false),那么你的检索将失败。
  • 除非那是你想要的。 ;) 如果它必须是可变的,您可以删除密钥,更新它并重新添加它。如果您更改键而它是映射的键(或出于相同原因的 Set 的元素),则唯一的问题是
  • 是的,在这种情况下,这不是我的意图。
【解决方案3】:

简单地说,HashMap 是计算机内存中包含键和值的对象。每个键都是唯一的(阅读哈希码),每个键都指向一个值。

在您的代码示例中,在每种情况下从您的地图中得出的值都是相同的,因为键是相同的。当您更改您的键时,无法获取它的值,因为您从未使用变异键将项目添加到您的 HashMap。

如果您添加了该行:

map.put("mutated", 2);

在改变键之前,你将不再获得空值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 2016-04-20
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    相关资源
    最近更新 更多