【问题标题】:Hashcode and Equals implementationHashcode 和 Equals 实现
【发布时间】:2018-04-24 08:58:02
【问题描述】:

考虑下面的HashMap 实现

HashMap<String,String> hashMap=new HashMap<>();
hashMap.put(new String("ABC"), "Hello");
hashMap.put("ABC", "Hello");
System.out.println(hashMap.size());

代码如何将 size 返回为 1 以及如何在内部对其进行评估?但是,如果我使用 StringBuffer 而不是 String 代码返回值为 2。 这背后的原因是什么?

【问题讨论】:

  • HashMap先用对象的hashCode()再用equals()做比较。
  • 如果hashCode方法相等,并且equals方法返回true。
  • 当我打印我试图放入映射中的两个键的哈希码时,它们是相等的,这说明它们将存储在同一个存储桶中,并且 .equals 将是评估。但是现在我有一个问题,当我使用不同的字符串初始化方法时,哈希码的计算结果是一样的。
  • 尝试new String("ABC").equals("ABC")new StringBuffer("ABC").equals("ABC") - 第一个语句应该返回真,而第二个是假。
  • 这个问题的标题是“Hasmap implementation”,但实际上你的问题是关于java.util.HashMap 中的equals()hascode() 实现。你介意改一下吗?

标签: java hash hashmap


【解决方案1】:

HashMaps 使用hashCode 方法确定存储桶,然后使用equals 确定任何现有对象是否与要添加的对象相同。在您的情况下,"ABC"new String("ABC") 将具有相同的 hashCode 并且将被 String.equals 视为相等,因此这两个对象将被视为相同。

如果您不希望出现这种情况,那么您可以考虑使用IdentityHashMap,它使用对象引用来比较是否相等。

【讨论】:

  • 小心使用带有字符串键的 IdentityHashMap。字符串实习可能会导致意外行为!
【解决方案2】:

HashMap 使用Object.equals(other) 作为两个对象是否应该算作“相同”的最终仲裁者。使用 String 时,equals 有效地比较了 String 的字符内容,因此 "ABC".equals(new String("ABC")); 将返回 true

另一方面,

StringBuffer 使用 Object 的默认实现 equals,它基本上检查两个对象是否引用相同(即它们都指向完全相同的内存地址 - 这是== 对 Java 中的对象做了什么)。用两个不同的 new 语句构造的两个 StringBuffers 永远不会引用相同,因此永远不会是 equal。这是有充分理由的:大多数时候,我们希望 Objects 的 equals 方法对给定参数返回相同的答案,无论何时调用它。由于可以操作 StringBuffer 的内容,因此两个缓冲区可能在某个时间点具有相同的内容,但随后在其中一个被附加到之后具有不同的内容。

StringBuffer 是可变的这一事实可能使它不适合用作映射的键​​。除非您拥有与您最初put 相同的完全相同 StringBuffer,否则您将无法在地图上执行查找。相反,如果出于某种原因想要这种行为,则可以使用IdentityHashMap,它将始终使用对象的内存地址来确定它是否与另一个对象相等,而不管其类型如何。不过要小心使用带有字符串键的 IdentityHashMap - 因为string interning,在代码中的不同位置构造(从文字)的 2 个等效字符串 实际上指向同一块内存,所以将参考相同。

【讨论】:

    【解决方案3】:

    您缺少输入 HashMap&lt;String,String&gt; hashMap=new HashMap&lt;&gt;();。如果您想创建String, String 的映射,那么您将无法创建StringBuilder 的对象作为键。如果您想同时支持两者,则可以使用Object 而不是String 作为键。 如果你想支持StringStringBuilder,那么你的代码将是

    HashMap<Object,String> hashMap=new HashMap<Object, String>();
    

    以下是尺寸差异的解释。

    “任何值”被视为String 类的实例。 StringBuilderString 不同。在您的代码中,同一类的对象被创建为键,这会产生相同的 hashCode(请参阅 String.java 中的 hashCode() 函数)。但是,StringBuilder 使用了来自 Object.java 的 hashCode()。因此,它们具有不同的 hashCode 并得到相应的处理。

    【讨论】:

      【解决方案4】:

      HashMap 的 put(K key, V value) 方法首先通过 key.hashCode()%m 哈希到桶中,然后遍历 Map.Entry 对象的单链表来识别它 密钥通过 key.equals(k) 存在。如果它找到一个键,它会更新该条目的值,否则它会将键插入到链表的前面

      1) 对于 StringBuffer 情况

          (i) new StringBuffer("ABC").equals(new StringBuffer("ABC")) is false
         (ii) new StringBuffer("ABC")!=new StringBuffer("ABC").hashCode() is false
      

      这是因为 StringBuffer 没有覆盖并使用Objects equals(Object o) 和 hashCode() 。 所以它们是地图中的两个不同的条目

      2) 字符串大小写

        (i) "ABC".equals(new String("ABC")) is true
       (ii) "ABC".hashCode()==new String("ABC").hashCode() is true
      

      所以这两个是地图中的一个条目

      【讨论】:

        【解决方案5】:

        我认为你需要了解什么是 hashmap, Equal 和 hashcode 用于从分配的内存位置存储和检索对象。 因此,在您的情况下,使用内容验证的密钥不是使用 keyObjects 引用,因为它是字符串。即使您创建 String="ABC" 文字或 new String("ABC"); 在这种情况下,Equals 方法的结果为真,因此现有密钥替换为新密钥。它是 SET 的基本概念。 HashMap 使用 SET 作为键。因此,当我们尝试添加匹配的新键时,将使用 equals 方法替换现有键。 出于这个原因,我们应该正确地覆盖 equals 和 hashcode 方法。 规则: 如果两个对象相等,那么它们必须具有相同的哈希码。 如果两个对象具有相同的哈希码,它们可能相等也可能不相等。

        【讨论】:

          猜你喜欢
          • 2019-06-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-22
          • 1970-01-01
          • 2020-02-21
          • 1970-01-01
          相关资源
          最近更新 更多