【问题标题】:How HashSet works with regards to hashCode()?HashSet 如何处理 hashCode()?
【发布时间】:2014-09-04 17:51:26
【问题描述】:

我正在尝试更深入地了解 java.util.Collection 和 java.util.Map,但我对 HashSet 功能有一些疑问:

在文档中,它说:这个类实现了 Set 接口,由一个哈希表(实际上是一个 HashMap 实例)支持。 好的,所以我可以看到一个 HashSet 总是有一个 Hashtable 工作在背景中。哈希表是一个结构,每次你想向它添加一个新元素时,它都会请求一个键和一个值。然后,将值和键存储在基于键 hashCode 的存储桶中。如果两个键的哈希码相同,则它们使用链表将两个键值添加到同一个桶中。如果我说错了,请纠正我。

所以,我的问题是:如果一个 HashSet 总是有一个 Hashtable 在后台运行,那么每次我们使用 HashSet.add() 方法向 HashSet 添加一个新元素时,HashSet 都应该将它添加到它的内部 Hashtable 中。但是,Hashtable 要求一个值和一个键,那么它使用什么键呢?它是否只是使用我们尝试添加的值作为键,然后获取它的 hashCode?如果我对 HashSet 实现说错了,请纠正我。

我的另一个问题是:一般来说,哪些类可以使用 java 对象的 hashCode() 方法?我问这个是因为,在文档中,它说每次我们覆盖 equals() 方法时,我们都需要覆盖 hashCode() 方法。好的,这确实有道理,但我怀疑这是否只是我们应该做的建议以保持一切“美好和完美”(以这种方式),或者是否真的有必要,因为可能很多 Java 默认类会不断使用对象的 hashCode() 方法。在我看来,我看不到使用此方法的其他类,而不是那些与 Collections 相关的类。非常感谢你们

【问题讨论】:

  • 您添加到集合中的“值”实际上是地图的HashSet 放入地图的值无关紧要。它实际使用的值是对名为PRESENT 的对象的虚拟对象引用。

标签: java hashmap hashcode hashset


【解决方案1】:

如果您查看 HashSet 的实际 javacode,您可以看到它的作用:

 // Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
...

 public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

因此,您要添加的元素是后备哈希图中的 Key,其值为虚拟值。 hashSet 从未真正使用过这个虚拟值。

关于覆盖等于和哈希码的第二个问题:

如果您想覆盖其中任何一个,则确实有必要始终覆盖两者。这是因为 hashCode 的合约规定相等的对象必须具有相同的 hashcode。 hashcode 的默认实现会为每个实例提供不同的值。

因此,如果你覆盖 equals() 而不是 hashcode() 这可能会发生

object1.equals(object2) //true

MySet.add(object1);

MySet.contains(object2); //false but should be true if we overrode hashcode()

由于 contains 将使用哈希码来查找要搜索的存储桶,因此我们可能会返回不同的存储桶而找不到相同的对象。

【讨论】:

  • 非常感谢!所以,根据你说的,如果我们添加到 HashSet 的值作为其内部 HashMap 中的键,那么每次我们创建一个特定类型 Object 的 HashSet,那么这个类型的 hashCode() 方法应该是如果我们想获得最大的性能,那么重写并运行良好,对吗?最后一个问题是:hashcode() 对列表也很重要?(例如 ArrayList)名称中没有“Hash”的集合,例如 TreeSet?我猜 TreeSet 是使用树而不是哈希表实现的,所以 hashCode() 是否相关?
  • ArrayListLinkedListTreeSet 未使用哈希码。它由基于散列的集合HashMapHashSetConcurrentHashMap 等使用。您可以随时查看javadoc 中的集合类。它应该记录它是否是基于哈希的。
  • @David Conrad 非常感谢!你们解决了我所有的疑惑:)
【解决方案2】:

如果您查看HashSet 的源代码(该源代码随 JDK 一起提供,信息量很大),您会看到它创建了一个用作值的对象:

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

添加到HashSet 的每个值都用作支持HashMap 的键,并以此PRESENT 对象为值。

关于在覆盖hashCode() 时覆盖equals()(反之亦然),这两种方法返回一致的结果非常重要。也就是说,他们应该彼此同意。有关详细信息,请参阅 Josh Bloch 的书 Effective Java

【讨论】:

  • 非常感谢。这帮助很大
  • @user3513453 如果答案对您有帮助,您可以投票。
  • 其实不行,我需要15声望:P
猜你喜欢
  • 2017-11-19
  • 2016-08-29
  • 2016-05-28
  • 1970-01-01
  • 2023-02-14
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2014-02-09
相关资源
最近更新 更多