【问题标题】:Having HashMap inside a HashMap - call by reference or duplicate keys?在 HashMap 中有 HashMap - 通过引用或重复键调用?
【发布时间】:2016-07-10 16:08:14
【问题描述】:

我只是想知道,当有一个 HashMap<HashMap<Integer, Integer>, String> 并且我添加一个新的 HashMap 作为键时,它是否被视为 重复键 或者我们有一个引用调用并且值不是看过了吗?

谢谢:)

【问题讨论】:

  • 正常情况下,Java 根本不使用按引用传递,而是按值传递引用。我不完全确定您在问什么,但这听起来确实像是您可以轻松地为自己测试的东西。一般来说,使用地图作为 key 是一件很奇怪的事情。
  • 据我了解您的问题:整数是哈希键,但它应该只有 1 个字符串映射到它。
  • @Bonatti,哈希键是具有两个整数作为键和值的 HashMap,我的问题是,当添加新的哈希图作为键时,它们是覆盖现有的 HashMap,还是添加为新钥匙?添加新的HashMap后,HashMap的大小是增加还是保持为1?
  • 如果我猜对了你的意图,你的意思是问是否在外部 HashMap 中放置一个新的 HashMap 键是否通过引用比较或实际将 HashMap 的内容与内容进行比较来检查该键是否已经存在现有的密钥。由于 HashMap 覆盖了 hashCode 和 equals,所以答案是后者。添加的密钥的内容确定它是否与现有密钥相同。
  • 如果键不同,则添加它,如果它等于已存在的键,则覆盖

标签: java data-structures hashmap pass-by-reference


【解决方案1】:

@Stephen 给出了非常好的概念性解释。 简而言之:

  • 当我们在 HashMap 中“放入”某些东西时,它在内部存储在一个“table”中,它是一个“Entry”数组"
  • 现在它将存储在该表的哪个位置 (bucketIndex) 将根据要存储的键的“hashcode”来决定。在存储之前JVM 将检查该 bucketIndex 中是否存在任何“Entry”。当两个“键”具有相同的哈希码时,这种情况是可能的。现在如果有一个 入口 JVM 将进一步检查“key”本身是否与“”已经存在。如果是,它会将其视为重复键,并在“Entry”中更新其各自的值。如果不是,它只会在相同的 bucket Index 中添加另一个条目。
  • 在通过发送“key”从地图中“获取”值时,将运行类似的过程。首先“hashcode " 将被提取,并据此确定要查看的表的位置 (bucketIndex)。现在,如果该索引中没有内容,将返回“null”。否则...
  • JVM 将转到该索引,并且可能存在多个“Entry”,因为多个对象可以具有相同的哈希码。现在 JVM 将在该 key 上调用“equals”方法来检查表中存在的 Key 是否与为检索值而发送的 inputKey 相同。如果 "equals" 返回 true,那么我们将获得所需的值。

所以一般来说,当且仅当 hashcode() 两者都返回相同的值并且 等于 时,一个键将被视为重复键 >() 将返回 true。

现在来回答你的问题“我添加一个新的 HashMap 作为键,它是否被视为重复键”答案是肯定的,当且仅当你的新 HashMap 具有精确的与现有 HashMap 键相同的条目,即键、值对。

因为:

  1. 如果你看一下HashMaphashcode()实现,你会看到它的hashcode是根据“Key”和“value”计算出来的。所以如果 2 个 hashmap 将具有相同的 集,则它们将具有相同的 hashcode()
  2. HashMap 的 equals() 检查 Entry 是否相同。因此,如果两个 Hashmap 具有相同的 集,则它们是相等的。

现在看下面的代码演示这个概念:

public static void main(String[] args) {
    // TODO Auto-generated method stub
    HashMap<Integer,Integer> keyMap=new HashMap<Integer, Integer>();
    keyMap.put(2, 1020);
    keyMap.put(3, 1352);
    keyMap.put(23,1256);
    System.out.println("hashcode keymap1:"+keyMap.hashCode());
    HashMap<Integer,Integer> keyMap2=new HashMap<Integer, Integer>();
    keyMap2.put(1, 100);
    keyMap2.put(4, 152);
    keyMap2.put(43,156);
    System.out.println("hashcode keymap2:"+keyMap2.hashCode()); 
    HashMap<HashMap<Integer,Integer>,String> mainMap=new HashMap<HashMap<Integer,Integer>,String>();
    mainMap.put(keyMap, "1st value");
    mainMap.put(keyMap2, "2ndvalue");
    System.out.println(mainMap);
    HashMap<Integer,Integer> keyMap3=new HashMap<Integer, Integer>();
    keyMap3.put(23,1256);
    keyMap3.put(3, 1352);
    keyMap3.put(2, 1020);
    System.out.println("hashcode keymap3:"+keyMap3.hashCode());
    mainMap.put(keyMap3, "3rd value");
    System.out.println(mainMap);
    if(mainMap.containsKey(keyMap3))
        System.out.println(" value retrieved is :"+mainMap.get(keyMap3));
    else
        System.out.println("key not found");

}

在这里您可以观察到 keymapkeymap3 具有相同的 key、value 和相同的 hashcode 集。所以两者都在这里 duplicate key,因此 keymap 的值由 keymap3 的值更新。

【讨论】:

    【解决方案2】:

    HashMap.equals(Object) 方法的约定是:

    "比较指定的对象和这个映射是否相等。如果给定的对象也是一个映射并且两个映射表示相同的映射,则返回true。更正式地说,如果m1,两个映射m1和m2表示相同的映射.entrySet().equals(m2.entrySet())。”

    现在 Map 的标准行为是如果 equals(Object) 表示它们相等,则将键视为相同。

    所以你的问题的答案是,如果你有

      HashMap<Integer, Integer> k1 = // some map
      HashMap<Integer, Integer> k2 = // another map      
      HashMap<HashMap<Integer, Integer>, String> map = // some
    

    然后使用k1k2 作为map 中的键,如果k1.equals(k2) 给你一个条目,否则给你两个条目。

    鉴于k1k2 是映射,我们通过比较它们各自的映射条目集来确定它们是否相等。


    这有两个明显的问题:

    1. 如果您更改k1k2 而它们是map 中条目的键,那么您将破坏map 的基本不变量。发生这种情况时,您会发现对map 的操作会给出不正确的结果;例如map.get(k1) 会给出错误的答案。

    2. 每当您执行涉及在map 上查找的操作时,您将调用HashMap.hashCode() 以获取关键对象。计算键的哈希码需要计算映射HashMap&lt;Integer, Integer&gt; 中每个键和值的哈希码。这很昂贵,特别是因为这个 HashMap.hashCode() 不(不能)缓存任何东西。

    简而言之,使用HashMap 作为另一个HashMap 的密钥是个坏主意。


    所以,回答你的问题:

    我只是想知道,当有一个HashMap&lt;HashMap&lt;Integer, Integer&gt;, String&gt; 并且我添加一个新的 HashMap 作为键时,它是否被视为重复键或者我们有一个引用调用a 并且值为没看过?

    1. 它不会是重复的键b,除非相应的键是具有相同条目集的映射。这就是HashMap.equals(Object) 测试的内容。

    2. 不作为参考进行比较;即它不与 == 语义进行比较。 HashMap.equals(Object) 方法用于比较。


    a - 请注意,“引用调用”术语不适用于这种情况。引用调用/值调用是关于方法调用时如何传递参数的。

    b - .... 前提是您不违反不变量。

    【讨论】:

      猜你喜欢
      • 2017-07-11
      • 1970-01-01
      • 2015-03-30
      • 2015-05-12
      • 1970-01-01
      • 2013-12-26
      • 2013-02-26
      • 2023-03-19
      • 1970-01-01
      相关资源
      最近更新 更多