HashSet如何处理hashCode()?

时间:2014-07-14 18:02:28

标签: java hashmap hashcode hashset

我试图更深入地理解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()方法。在我的视野中,我无法看到使用此方法的其他类而不是与集合相关的类。非常感谢你们

2 个答案:

答案 0 :(得分:11)

如果你看看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;
}

因此,您添加的元素是支持hashmap中的Key,其值为虚拟值。这个虚拟值永远不会被hashSet实际使用。

关于覆盖equals和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将使用hashcode来查找要搜索的存储桶,我们可能会获得一个不同的存储桶而不能找到相同的对象。

答案 1 :(得分:2)

如果您查看HashSet的源代码(源代码随JDK提供并且信息非常丰富),您将看到它创建了一个用作值的对象:

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

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

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