【问题标题】:Does a HashMap use a HashSet to store its keys?HashMap 是否使用 HashSet 来存储其键?
【发布时间】:2014-04-30 03:01:01
【问题描述】:

我想知道 HashMap 是否使用 HashSet 来存储其键。我猜是这样,因为 HashMap 对应于 HashSet,而 TreeMap 对应于 TreeSet。

我查看了 HashMap 类的源代码,该方法返回了一个由某种迭代器实现的 AbstractSet。

另外...当我写的时候

    HashMap map = new HashMap();

    if(map.keySet() instanceof HashSet){
        System.out.println("true");
    }

上面的 if 语句永远不会运行。现在我不确定

有人能解释一下 HashMap 是如何存储它的键的吗?

【问题讨论】:

  • 不,[在 Oracle JDK 中] 它没有。 Look at the implementation (on GrepCode),这应该很明显 HashMap 没有使用和底层的 HashSet。 (另外from the API你可以看到HashMap不是 HashSet的子类型,甚至也不符合Set接口。)
  • (非常重要,请参阅 HashMap 中的 Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; 成员 - 使用的是数组,而不是 HashSet。)
  • @user2864740 请注意,您的链接是 OpenJDK 实现,而不是 Oracle JDK。
  • 所以您已经查看了HashMap 的来源,所以您知道答案是否定的。有趣的是,HashSet 将其值作为键存储在 HashMap 中,但这是一个不同的问题。
  • @dimo414 也就是说,您看到的差异来自 7-update 和 8 版本中代码的演变,而不是 OpenJDK 和 Oracle JDK 之间的分歧。

标签: java hashmap


【解决方案1】:

您实际上是在问两个不同的问题:

  1. HashMap 是否使用HashSet 来存储其密钥?
  2. HashMap.keySet() 是否返回 HashSet

这两个问题的答案是否定的,原因相同,但没有技术原因阻止 1. 或 2. 为真。

HashSet 实际上是HashMap 的包装; HashSet 有以下成员变量:

private transient HashMap<E,Object> map;

当一个对象被添加到集合中时,它会填充一个PRESENT标记值作为映射的值。

现在HashMap 将其数据存储在一个包含键值对的Entry 对象数组中:

transient Entry<K,V>[] table;

而且它的keySet()方法返回内部类KeySet的实例:

public Set<K> keySet() {
  Set<K> ks = keySet;
  return (ks != null ? ks : (keySet = new KeySet()));
}

private final class KeySet extends AbstractSet<K> {
  // minimal Set implementation to access the keys of the map
}

由于KeySet 是一个私有内部类,就您而言,它只是一个任意的Set 实现。

就像我说的,没有理由必须是这样。您绝对可以实现一个在内部使用HashSetMap 类,然后让您的Map.keySet() 返回一个HashSet。然而,这将是低效且难以编码的;现有的实现比幼稚的Map/Set 实现更健壮、更高效。

取自 Oracle JDK 1.7.0_17 的代码 sn-ps。您可以在 Java 安装目录的 src.zip 文件中查看您的 Java 版本的源代码。

【讨论】:

    【解决方案2】:

    我想知道 HashMap 是否使用 HashSet 来存储其键。

    这不会很好,因为 Set 只跟踪键。它无法存储关联的值映射。

    相反的(使用 Map 来存储 Set 元素)是可能的,并且正在使用这种方法:

    HashSet 是通过使用 HashMap 实现的(所有键都有一个虚拟值)。

    HashMap#keySet 返回的密钥集由私有内部类 (HashMap.KeySet extends AbstractSet) 实现。

    您可以研究这两个类的源代码,例如在 GrepCode 上:HashMapHashSet

    有人能解释一下 HashMap 是如何存储它的键的吗?

    它使用一个桶数组。每个存储桶都有一个条目链接列表。另见

    【讨论】:

    • 在JDK 8中,如果链表太长,桶会被转换成树。
    • 参见my answer另一个问题,了解从列表切换到树的策略说明。
    【解决方案3】:

    keySet 返回的集合仅由底层映射支持。

    根据 javadoc

    返回此映射中包含的键的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作除外),则迭代的结果是不确定的。该集合支持元素移除,即通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作从映射中移除相应的映射。它不支持 add 或 addAll 操作。 块引用

    HashMap 将键存储到桶中。具有相同哈希码的键进入同一个桶。如果在桶中找到多个键,则在检索键的值时,使用 equals 方法查找正确的键,从而找到正确的值。

    【讨论】:

      【解决方案4】:

      答案是:没有。

      HashMap.keySet() 是此映射中包含的键的VIEW

      地图的数据存储在HashMapEntry[]表中。

      【讨论】:

        猜你喜欢
        • 2015-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-06
        • 2010-12-10
        相关资源
        最近更新 更多