Java集合:如果Hashmap的键是Immutable Object,那么我们是否需要覆盖hashcode和equals?

时间:2014-05-13 19:22:29

标签: java collections hashmap equals hashcode

我对一个概念感到困惑。有人可以请点亮它。

问题:如果Hashmap的密钥是不可变对象(由开发人员创建),那么我们是否需要覆盖hashcode()equals()?或者将不可变字段作为键来解决覆盖hashcode()equals()的问题?

感谢。

4 个答案:

答案 0 :(得分:4)

是。我在这里引用java.lang.Integer的例子。如果我们希望将整数(稀疏)映射到对象,我们会使用HashMap<Integer, Object>行的内容。如果我们添加条目Integer.valueOf(2)=>"foo",并尝试使用new Integer(2)检索它,则需要重写的哈希码和等号。

答案 1 :(得分:1)

这些问题略有不同。

hexafraction's answer一样,如果两个不同的实例可以被认为是相同的,那么使用不可变实例不足以让您跳过编写equalshashCode的步骤。 new Integer(2)应始终等于其他new Integer(2),即使对象是不可变的且实例不同。

也就是说,有一些&#34;实例控制的类&#34;其中实例标识的默认行为就足够了:

  • 在编译时创建枚举实例,每个值一个。 (理论上)没有办法产生任何其他实例。如果没有两个实例相等,则equalshashCode的默认实现就足够了。 Enum instances aren't compiler-guaranteed to be immutable, but you should treat them as such.

  • 如果您的班级实例保证彼此不同,无论他们是否不可变,您都可以跳过equalshashCode。可以想象一个Car对象,其中CarFactory生成的每辆Car都不同。

  • 作为上述内容的一种变体,如果你足够紧密地控制对象实例化,相同的表示总是给出完全相同的实例,那么这可以被认为是足够的:

    public class MyClass {
      private MyClass(int foo) { /* ... */ }
    
      private static final Map<Integer, MyClass> instanceCache = new HashMap<>();
    
      /** Returns an existing MyClass(foo) if present; otherwise, creates one. */
      public synchronized static MyClass create(int foo) {
        // Neither leak-proof or thread-safe. Just demonstrating a concept.
        if (instanceCache.contains(foo)) {
          return instanceCache.get(foo);
        }
        MyClass newMyClass = new MyClass(foo);
        instanceCache.put(foo, newMyClass);
        return newMyClass;
      }
    }
    

答案 2 :(得分:0)

试一试,看看:

public class OverrideIt
{
    static class MyKey
    {
        public final int i; // immutable
        public MyKey(int i)
        {
            this.i = i;
        }
    }

    public static void main(String[] args)
    {
        Map<MyKey, String> map = new HashMap<MyKey, String>();
        map.put(new MyKey(1), "Value");
        String result = map.get(new MyKey(1));
        System.out.println(result);  // null
    }
}

打印出null,表明我们无法查找我们的值。这是因为MyKey的两个副本不相等且没有相同的哈希码,因为我们没有覆盖.equals()hashcode()

答案 3 :(得分:0)

事实

  1. hashMap的基本数据结构是Entry[](条目是LinkedList的种类)。
  2. 用于定位此数组中位置的键的哈希码
  3. 使用哈希码重试Entry后,Key等于用于选择正确的条目(通过迭代hasNext)
  4. 默认哈希码返回该实例的唯一整数值​​。
  5. 你在评论部分同意

     "Is it possible that you'll store an object using one key, 
      and then try to retrieve it using a key which is an identical object,
      but not the same object"
    

    即使两个键具有相同的值,实例也是不同的。那么你可能根据合同对两个密钥都有不同的哈希码(事实4)。因此,您将在数组中具有不同的位置(规则2)

    map.put(new key(1),"first element");

    此处,键对象不会覆盖,因此它将返回每个实例的哈希码unquie。 (为了避免过于复杂化,假设哈希码返回为000025.所以Entry [25]是&#34; First Element&#34;)

    map.get(new key(1))

    现在这个新密钥可能会将哈希码值返回为000017,所以它会尝试从Entry [17]获取值并返回null(不例外)。

    注意为了简单起见,我只是将示例作为000025和000017,实际上hashmap会重新访问哈希码并根据数组大小进行更改

    到目前为止,我们还没有讨论过Key是可变的还是不可变的。无论密钥是可变的还是不可变的

    If you store an object using one key,and then try to retrieve it using a key 
    which is an identical object,but not the same object
    

    您需要覆盖哈希码并确保它返回相同的整数,以便它可以找到相同的桶(在Array中的位置)并获取该元素。同样适用于从条目中获取正确元素的等于