进入“ on / off主题”的灰色区域,但是有必要消除关于Oscar Reyes的困惑,因为更多的哈希冲突是一件好事,因为它减少了HashMap中的元素数量。我可能会误解Oscar在说什么,但我似乎并不是唯一一个:kdgregory,delfuego,Nash0,而且我似乎都拥有相同的(误解)理解。
如果我理解Oscar对于具有相同哈希码的相同类的说法,他建议仅将具有给定哈希码的类的一个实例插入到HashMap中。例如,如果我有一个哈希码为1的SomeClass实例和另一个哈希码为1的SomeClass实例,则仅插入一个SomeClass实例。
http://pastebin.com/f20af40b9上的Java pastebin示例似乎表明上述内容正确地总结了Oscar的建议。
无论有什么理解或误解,发生的事情是,如果相同类的不同实例具有相同的哈希码,则它们不会仅一次插入到HashMap中-直到确定键是否相等为止。哈希码协定要求相等的对象具有相同的哈希码;但是,不要求不相等的对象具有不同的哈希码(尽管出于其他原因这可能是理想的)[1]。
随后是pastebin.com/f20af40b9示例(Oscar至少引用了两次),但对其进行了少许修改以使用JUnit断言而不是打印行。此示例用于支持以下建议:相同的哈希码会导致冲突,并且当类相同时,仅创建一个条目(例如,在此特定情况下,仅创建一个String):
@Test
public void shouldOverwriteWhenEqualAndHashcodeSame() {
    String s = new String("ese");
    String ese = new String("ese");
    // same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    // same class
    assertEquals(s.getClass(), ese.getClass());
    // AND equal
    assertTrue(s.equals(ese));
    Map map = new HashMap();
    map.put(s, 1);
    map.put(ese, 2);
    SomeClass some = new SomeClass();
    // still  same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    assertEquals(s.hashCode(), some.hashCode());
    map.put(some, 3);
    // what would we get?
    assertEquals(2, map.size());
    assertEquals(2, map.get("ese"));
    assertEquals(3, map.get(some));
    assertTrue(s.equals(ese) && s.equals("ese"));
}
class SomeClass {
    public int hashCode() {
        return 100727;
    }
}
但是,哈希码并不是完整的故事。pastebin示例忽略了一个事实,即两者s和ese都相等:它们都是字符串“ ese”。因此,使用s或ese或"ese"作为键插入或获取地图的内容都是等效的,因为s.equals(ese) && s.equals("ese")。
第二个测试表明,得出结论,认为同一类上的相同哈希码是键->值s -> 1被测试一中的ese -> 2when 覆盖的原因是错误的map.put(ese, 2)。在试验二,s并且ese仍然有相同的哈希码(通过验证assertEquals(s.hashCode(), ese.hashCode());),他们是同一类。但是,s并且ese是MyString该测试中的实例,而不是Java String实例-与该测试相关的唯一区别是等于:String s equals String ese在上面的测试一中,而MyStrings s does not equal MyString ese在测试二中:
@Test
public void shouldInsertWhenNotEqualAndHashcodeSame() {
    MyString s = new MyString("ese");
    MyString ese = new MyString("ese");
    // same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    // same class
    assertEquals(s.getClass(), ese.getClass());
    // BUT not equal
    assertFalse(s.equals(ese));
    Map map = new HashMap();
    map.put(s, 1);
    map.put(ese, 2);
    SomeClass some = new SomeClass();
    // still  same hash right?
    assertEquals(s.hashCode(), ese.hashCode());
    assertEquals(s.hashCode(), some.hashCode());
    map.put(some, 3);
    // what would we get?
    assertEquals(3, map.size());
    assertEquals(1, map.get(s));
    assertEquals(2, map.get(ese));
    assertEquals(3, map.get(some));
}
/**
 * NOTE: equals is not overridden so the default implementation is used
 * which means objects are only equal if they're the same instance, whereas
 * the actual Java String class compares the value of its contents.
 */
class MyString {
    String i;
    MyString(String i) {
        this.i = i;
    }
    @Override
    public int hashCode() {
        return 100727;
    }
}
根据后来的评论,奥斯卡似乎改变了他先前所说的话,并承认平等的重要性。但是,似乎仍然很重要的概念是不清楚,而不是“同一阶级”(强调我的意思):
“不是真的。仅在哈希相同但键不同的情况下才创建列表。例如,如果String给出哈希码2345,而Integer给出相同的哈希码2345,则由于String,将整数插入列表。 equals(Integer)是false。但是,如果您具有相同的类(或至少.equals返回true),则使用相同的条目。例如,new String(“ one”)和`new String(“ one”)用作键,将使用相同的条目。实际上,这是HashMap的整个起点!亲自查看:pastebin.com/f20af40b9 – Oscar Reyes”
与先前的注释明确指出了相同的类和相同的哈希码的重要性,而没有提及equals:
“ @delfuego:自己看看:pastebin.com/f20af40b9因此,在这个问题中,使用了相同的类(请等待一分钟,使用相同的类对吗?),这意味着当使用相同的哈希时,应使用相同的条目被使用,并且没有条目“列表”。– Oscar Reyes”
要么
“实际上,这将提高性能。冲突越多,哈希表中的条目越少。要做的工作越少。不是我认为它在对象上的哈希(看起来不错)或哈希表(效果很好)表现令人失望的创作。–奥斯卡·雷耶斯(Oscar Reyes)
要么
“ @kdgregory:是的,但是仅当冲突发生在不同的类上时,对于相同的类(这种情况),将使用相同的条目。– Oscar Reyes”
再一次,我可能会误解奥斯卡实际上想说的话。但是,他的原始评论引起了足够的混乱,以至于通过一些明确的测试来清除所有内容似乎是谨慎的做法,因此没有挥之不去的疑问。
[1] -Joshua Bloch撰写的有效Java第二版:
- 只要在应用程序执行期间在同一个对象上多次调用它,则hashCode方法必须一致地返回相同的整数,前提是未修改在该对象的equals比较中使用的信息。从一个应用程序的执行到同一应用程序的另一执行,此整数不必保持一致。 
- 如果根据相等的s(Obj ect)方法两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。  
- 如果两个对象根据相等的s(Object)方法不相等,则不需要在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。