Es ist eine gute Sache, in die Grauzone des "Ein / Aus-Themas" zu gelangen, die jedoch notwendig ist, um Verwirrung hinsichtlich des Vorschlags von Oscar Reyes zu vermeiden, dass mehr Hash-Kollisionen auftreten, da dadurch die Anzahl der Elemente in der HashMap verringert wird. Ich mag falsch verstehen, was Oscar sagt, aber ich scheine nicht der einzige zu sein: kdgregory, delfuego, Nash0, und ich scheine alle dasselbe (falsche) Verständnis zu teilen.
Wenn ich verstehe, was Oscar über dieselbe Klasse mit demselben Hashcode sagt, schlägt er vor, dass nur eine Instanz einer Klasse mit einem bestimmten Hashcode in die HashMap eingefügt wird. Wenn ich beispielsweise eine Instanz von SomeClass mit einem Hashcode von 1 und eine zweite Instanz von SomeClass mit einem Hashcode von 1 habe, wird nur eine Instanz von SomeClass eingefügt.
Das Java-Pastebin-Beispiel unter http://pastebin.com/f20af40b9 scheint darauf hinzudeuten, dass das oben Gesagte richtig zusammenfasst, was Oscar vorschlägt.
Unabhängig von Verständnis oder Missverständnissen passieren verschiedene Instanzen derselben Klasse nicht nur einmal in die HashMap eingefügt, wenn sie denselben Hashcode haben - erst, wenn festgestellt wird, ob die Schlüssel gleich sind oder nicht. Der Hashcode-Vertrag erfordert, dass gleiche Objekte denselben Hashcode haben. Es ist jedoch nicht erforderlich, dass ungleiche Objekte unterschiedliche Hashcodes haben (obwohl dies aus anderen Gründen wünschenswert sein kann) [1].
Das Beispiel pastebin.com/f20af40b9 (auf das Oscar mindestens zweimal verweist) folgt, wurde jedoch geringfügig geändert, um JUnit-Zusicherungen anstelle von Druckzeilen zu verwenden. Dieses Beispiel wird verwendet, um den Vorschlag zu unterstützen, dass dieselben Hashcodes Kollisionen verursachen. Wenn die Klassen identisch sind, wird nur ein Eintrag erstellt (z. B. nur ein String in diesem speziellen Fall):
@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;
}
}
Der Hashcode ist jedoch nicht die vollständige Geschichte. Was das Pastebin-Beispiel vernachlässigt, ist die Tatsache, dass beide s
und ese
gleich sind: Sie sind beide die Zeichenfolge "ese". So Einsetzen oder den Inhalt der Karte immer mit s
oder ese
oder "ese"
als Schlüssel sind alle gleichwertig , weils.equals(ese) && s.equals("ese")
.
Ein zweiter Test zeigt, dass es falsch ist, zu dem Schluss zu kommen, dass identische Hashcodes in derselben Klasse der Grund dafür sind, dass der Schlüssel -> Wert s -> 1
überschrieben wird, ese -> 2
wenn map.put(ese, 2)
er in Test 1 aufgerufen wird. In Test zwei s
und haben ese
immer noch den gleichen Hashcode (wie durch verifiziert assertEquals(s.hashCode(), ese.hashCode());
) UND sie sind die gleiche Klasse. Jedoch s
und ese
sind MyString
Instanzen in diesem Test nicht Java - String
Instanzen - mit dem einzigen Unterschied relevant für diesen Test der Gleichen zu sein: String s equals String ese
in Test oben, während MyStrings s does not equal MyString ese
in Test zwei:
@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;
}
}
Basierend auf einem späteren Kommentar scheint Oscar das, was er zuvor gesagt hat, umzukehren und erkennt die Bedeutung von Gleichberechtigten an. Es scheint jedoch immer noch unklar zu sein, ob Gleichheit wichtig ist, nicht die "gleiche Klasse" (Hervorhebung von mir):
"Nicht wirklich. Die Liste wird nur erstellt, wenn der Hash derselbe ist, der Schlüssel jedoch unterschiedlich. Wenn beispielsweise ein String den Hashcode 2345 und eine Ganzzahl den gleichen Hashcode 2345 angibt, wird die Ganzzahl wegen String in die Liste eingefügt. equals (Integer) ist false. Wenn Sie jedoch dieselbe Klasse haben (oder mindestens .equals true zurückgibt), wird derselbe Eintrag verwendet. Beispielsweise werden new String ("one") und `new String (" one ") als verwendet Schlüssel, wird den gleichen Eintrag verwenden. Eigentlich ist dies der GANZE Punkt von HashMap an erster Stelle! Überzeugen Sie sich selbst: pastebin.com/f20af40b9 - Oscar Reyes "
im Vergleich zu früheren Kommentaren, die sich explizit mit der Bedeutung identischer Klassen und desselben Hashcodes befassen, ohne dass Gleiches erwähnt wird:
"@delfuego: Überzeugen Sie sich selbst: pastebin.com/f20af40b9 In dieser Frage wird also dieselbe Klasse verwendet (Moment mal, dieselbe Klasse wird verwendet, oder?). Dies bedeutet, dass bei gleichem Hash derselbe Eintrag verwendet wird wird verwendet und es gibt keine "Liste" der Einträge. - Oscar Reyes "
oder
"Tatsächlich würde dies die Leistung erhöhen. Je mehr Kollisionen gleich weniger Einträge in der Hashtabelle sind, desto weniger Arbeit ist zu erledigen. Ist weder der Hash (der gut aussieht) noch die Hashtabelle (die gut funktioniert), würde ich wetten, dass er sich auf dem Objekt befindet Schöpfung, bei der die Leistung abnimmt. - Oscar Reyes "
oder
"@kdgregory: Ja, aber nur wenn die Kollision mit verschiedenen Klassen auftritt, wird für dieselbe Klasse (was der Fall ist) derselbe Eintrag verwendet. - Oscar Reyes"
Auch hier kann ich falsch verstehen, was Oscar tatsächlich zu sagen versuchte. Seine ursprünglichen Kommentare haben jedoch genug Verwirrung gestiftet, dass es ratsam erscheint, alles mit einigen expliziten Tests zu klären, damit keine Zweifel bestehen.
[1] - Aus Effective Java, 2. Auflage von Joshua Bloch:
Immer wenn es während der Ausführung einer Anwendung mehr als einmal für dasselbe Objekt aufgerufen wird, muss die hashCode-Methode konsistent dieselbe Ganzzahl zurückgeben, sofern keine Informationen geändert werden, die in Vergleichen gleicher Objekte für das Objekt verwendet werden. Diese Ganzzahl muss von einer Ausführung einer Anwendung zu einer anderen Ausführung derselben Anwendung nicht konsistent bleiben.
Wenn zwei Objekte gemäß der Methode same s (Obj ect) gleich sind, muss der Aufruf der hashCode-Methode für jedes der beiden Objekte das gleiche ganzzahlige Ergebnis liefern.
Es ist nicht erforderlich, dass beim Aufrufen der hashCode-Methode für jedes der beiden Objekte unterschiedliche ganzzahlige Ergebnisse erzielt werden müssen, wenn zwei Objekte gemäß der Methode equals s (Object) ungleich sind. Der Programmierer sollte sich jedoch bewusst sein, dass das Erzeugen unterschiedlicher ganzzahliger Ergebnisse für ungleiche Objekte die Leistung von Hash-Tabellen verbessern kann.