it-swarm.com.de

Was ist der Grund für Enum.hashCode ()?

Die Methode hashCode () in der Klasse Enum ist final und als super.hashCode () definiert. Dies bedeutet, dass sie eine Zahl zurückgibt, die auf der Adresse der Instanz basiert. Dies ist eine Zufallszahl des Programmierers POV.

Es zu definieren, z. as ordinal() ^ getClass().getName().hashCode() wäre für verschiedene JVMs deterministisch. Es würde sogar ein bisschen besser funktionieren, da sich die niedrigstwertigen Bits "so weit wie möglich ändern" würden, z. B. für eine Aufzählung mit bis zu 16 Elementen und eine HashMap mit der Größe 16, würde es mit Sicherheit keine Kollisionen geben (sicher, Die Verwendung einer EnumMap ist besser, aber manchmal nicht möglich (z. B. gibt es keine ConcurrentEnumMap). Mit der aktuellen Definition haben Sie keine solche Garantie, oder?

Zusammenfassung der Antworten

Die Verwendung von Object.hashCode() vergleicht sich wie folgt mit einem schöneren Hashcode wie dem obigen:

  • PROS
    • einfachheit
  • KONTRAS
    • geschwindigkeit
    • mehr Kollisionen (für jede Größe einer HashMap)
    • nichtdeterminismus, der sich auf andere Objekte ausbreitet und diese für unbrauchbar macht
      • deterministische Simulationen
      • ETag-Berechnung
      • die Suche nach Fehlern, abhängig von z. in einer HashSet Iterationsreihenfolge

Ich persönlich bevorzuge den schöneren hashCode, aber meiner Meinung nach macht kein Grund viel aus, vielleicht mit Ausnahme der Geschwindigkeit.

AKTUALISIEREN

Ich war neugierig auf die Geschwindigkeit und schrieb einen Benchmark mit überraschenden Ergebnissen . Für einen Preis für ein einzelnes Feld pro Klasse können Sie einen deterministischen Hash-Code verwenden, der fast viermal schneller ist. Das Speichern des Hash-Codes in jedem Feld wäre sogar noch schneller, wenn auch vernachlässigbar.

Die Erklärung, warum der Standard-Hash-Code nicht viel schneller ist, ist, dass er nicht die Adresse des Objekts sein kann, wenn Objekte vom GC verschoben werden.

UPDATE 2

Es gibt einige seltsame Dinge im Gange mit der Leistung von hashCode im Allgemeinen. Wenn ich sie verstehe, ist immer noch die Frage offen, warum System.identityHashCode (aus dem Objektheader lesen) viel langsamer ist als der Zugriff auf ein normales Objektfeld.

44
maaartinus

Ich denke, der Grund, warum sie es endgültig gemacht haben, ist, zu vermeiden, dass Entwickler sich selbst in den Fuß schießen, indem sie einen suboptimalen (oder sogar falschen) Hashcode schreiben.

In Bezug auf die gewählte Implementierung: Sie ist nicht für alle JVMs stabil, aber sie ist sehr schnell, vermeidet Kollisionen und benötigt kein zusätzliches Feld in der Enumeration. Angesichts der normalerweise geringen Anzahl von Instanzen einer Enum-Klasse und der Geschwindigkeit der Equals-Methode wäre ich nicht überrascht, wenn die HashMap-Suchzeit bei Ihrem Algorithmus aufgrund der zusätzlichen Komplexität größer wäre als beim aktuellen Algorithmus.

11
JB Nizet

Der einzige Grund, warum hashCode () von Object verwendet werden kann, um es endgültig zu machen, kann ich mir vorstellen, diese Frage zu stellen.

Zunächst sollten Sie sich nicht auf solche Mechanismen verlassen, um Objekte zwischen JVMs gemeinsam zu nutzen. Das ist einfach kein unterstützter Anwendungsfall. Beim Serialisieren/Deserialisieren sollten Sie sich auf Ihre eigenen Vergleichsmechanismen verlassen oder nur die Ergebnisse mit Objekten in Ihrer eigenen JVM "vergleichen".

Der Grund für das Implementieren, dass das Enum hashCode als Objects-Hashcode (basierend auf der Identität) implementiert wird, besteht darin, dass innerhalb einer JVM nur eine Instanz jedes Enum-Objekts vorhanden ist. Dies reicht aus, um sicherzustellen, dass eine solche Implementierung sinnvoll und korrekt ist.

Sie könnten argumentieren wie "Hey, String und die Wrapper für die Primitiven (Long, Integer, ...) haben alle genau definierte, deterministische Spezifikationen von hashCode! Warum haben es die Enums nicht?" Nun, anfangs können Sie mehrere eindeutige String-Referenzen haben, die denselben String darstellen, was bedeutet, dass die Verwendung von super.hashCode ein Fehler wäre, sodass diese Klassen notwendigerweise ihre eigenen Hash-Code-Implementierungen benötigen. Für diese Kernklassen war es sinnvoll, sie über definierte deterministische Hash-Codes verfügen zu lassen.

Warum entschieden sie sich, es so zu lösen?

Schauen Sie sich die Anforderungen der hashCode-Implementierung an. Das Hauptanliegen ist es sicherzustellen, dass jedes Objekt einen distinct Hashcode zurückgibt (es sei denn, er ist einem anderen Objekt gleich). Der identitätsbasierte Ansatz ist äußerst effizient und garantiert dies, während Ihr Vorschlag dies nicht tut. Diese Anforderung ist anscheinend stärker als jeder "Komfortbonus", wenn die Serialisierung beschleunigt werden soll.

24
aioobe

Ich habe die gleiche Frage gestellt, weil ich diese nicht gesehen habe. Warum verweist hashCode () in Enum auf die Implementierung von Object hashCode () anstelle der ordinal () - Funktion?

Ich habe es als eine Art Problem bei der Definition meiner eigenen Hash-Funktion für ein Objekt festgestellt, das auf enum hashCode als eine der Composites angewiesen ist. Bei der Überprüfung eines von der Funktion zurückgegebenen Werts in einem Satz von Objekten habe ich sie in einer Reihenfolge geprüft, die ich als gleich empfunden habe, da der Hashcode ich selbst definiere, und ich erwarte, dass Elemente an denselben Knoten fallen Da jedoch der von enum zurückgegebene hashCode von Anfang bis Anfang geändert wird, war diese Annahme falsch und der Test könnte gelegentlich fehlschlagen.

Als ich das Problem herausfand, begann ich stattdessen die Ordnungszahl zu verwenden.Ich bin nicht sicher, ob jeder, der hashCode für sein Objekt schreibt, dies erkennt.

Grundsätzlich können Sie also keinen eigenen deterministischen Hash-Code definieren, während Sie sich auf Enum-Hash-Code verlassen, und Sie müssen stattdessen eine Ordinalzahl verwenden.

P.S. Das war zu groß für einen Kommentar :)

2
mavarazy

Die JVM erzwingt das für eine Enumerationskonstante ist nur ein Objekt im Speicher vorhanden. Es gibt keine Möglichkeit, mit zwei verschiedenen Instanzobjekten derselben Enumenkonstante innerhalb einer einzelnen VM zu rechnen, nicht mit Reflektion, nicht über das Netzwerk durch Serialisierung/Deserialisierung.

Da dies das einzige Objekt ist, das diese Konstante darstellt, spielt es keine Rolle, dass der Hascode seine Adresse ist, da kein anderes Objekt gleichzeitig denselben Adressraum belegen kann. Es ist garantiert eindeutig und "deterministisch" (in dem Sinne, dass in derselben VM im Speicher alle Objekte die gleiche Referenz haben, egal was es ist).

1
pnt

Ich könnte mir vorstellen, dass dies so implementiert ist, weil hashCode () und equals () konsistent sind und dass das Konstruktionsziel von Enums einfach zu verwenden ist und die Konstante für die Kompilierungszeit (to) ist verwenden sie ist "case" Konstanten). Dies macht es auch legal, Enumerationsinstanzen mit "==" zu vergleichen, und Sie möchten einfach nicht, dass sich "Gleichgestellte" anders als "==" für Enummen verhält. Dies wiederum bindet hashCode an das standardmäßige referenzbasierte Object.hashCode () - Verhalten. Wie bereits erwähnt, erwarte ich auch nicht, dass equals () und hashCode () zwei Enumenkonstanten aus verschiedenen JVM als gleichwertig betrachten . Bei der Serialisierung: Bei Feldern, die als Enumerationen eingegeben werden, weist der standardmäßige binäre Serialisierer in Java ein besonderes Verhalten auf, bei dem nur der Name der Konstanten serialisiert wird. Bei der Deserialisierung wird der Verweis auf den entsprechenden Enumerationswert in der deserialisierenden JVM erneut erstellt . JAXB und andere XML-basierte Serialisierungsmechanismen funktionieren auf ähnliche Weise. Also: mach dir keine Sorgen

0
Mirko Klemm

Es ist nicht erforderlich, dass Hashcodes zwischen JVMs deterministisch sind, und es besteht kein Vorteil, wenn dies der Fall ist. Wenn Sie sich auf diese Tatsache verlassen, verwenden Sie sie falsch.

Da es nur eine Instanz jedes Aufzählungswerts gibt, ist garantiert, dass Object.hashcode() niemals kollidiert, guter Code wiederverwendet wird und sehr schnell ist.

Wenn Gleichheit durch Identität definiert wird, ergibt Object.hashcode() immer die beste Leistung.

Der Determinismus anderer Hash-Codes ist nur ein Nebeneffekt ihrer Implementierung. Da ihre Gleichheit normalerweise durch Feldwerte definiert wird, wäre das Mischen nicht deterministischer Werte Zeitverschwendung. 

0
OrangeDog

Solange wir kein Aufzählungsobjekt senden können1 zu einer anderen JVM sehe ich keinen Grund, solche Anforderungen an Aufzählungen (und Objekte im Allgemeinen) zu stellen


1 Ich dachte, es sei klar genug - ein Objekt ist eine Instanz einer Klasse. Ein serialisiertes Objekt ist eine Folge von Bytes, die normalerweise in einem Byte-Array gespeichert werden. Ich habe über ein Objekt gesprochen.

0
Andreas_D