Zusammengesetzte Objekte hashen

EDIT: This question is not about bitwise operators and can't be answered with Why are XOR often used in java hashCode() but another bitwise operators are used rarely?

Ich habe verschiedene Ansätze für die Hash-Berechnung von Objekten gesehen:

class A {
  public B b;
  public C c;

  @Override
  public boolean equals();
  @Override
  public int hashCode() {
   return c.hashCode() ^ b.hashCode(); //XOR
   return c.hashCode() + prime * b.hashCode();//SUM
   return Objects.hash(b,c);//LIB
  }
}

Es scheint, LIB-Methode verwendet SUM, aber warum ist es besser als XOR?

Obwohl das Beispiel in Java ist, geht es bei dieser Frage mehr um Mathematik und Wahrscheinlichkeiten.

11
hinzugefügt der Autor assylias, Quelle
hinzugefügt der Autor assylias, Quelle
Josh Bloch diskutiert eine gute Hashcode-Implementierung in effektivem Java .
hinzugefügt der Autor Edward Thomson, Quelle
Josh Bloch diskutiert eine gute Hashcode-Implementierung in effektivem Java .
hinzugefügt der Autor Edward Thomson, Quelle
Normalerweise benutzen Sie einfach die lib-Funktionen. Es sei denn, Sie führen eine Wahrscheinlichkeitsverteilungsanalyse durch, um zu bestimmen, wie Ihre Datenpunkte am besten verteilt werden. Finden Sie viele Kollisionen mit Ihrem Datensatz?
hinzugefügt der Autor CodeMonkeyForHire, Quelle
Normalerweise benutzen Sie einfach die lib-Funktionen. Es sei denn, Sie führen eine Wahrscheinlichkeitsverteilungsanalyse durch, um zu bestimmen, wie Ihre Datenpunkte am besten verteilt werden. Finden Sie viele Kollisionen mit Ihrem Datensatz?
hinzugefügt der Autor CodeMonkeyForHire, Quelle

12 Antworten

Die SUM stellt sicher, dass Sie alle Bits des Hashcodes verwenden, um Ihr Hashing zu verteilen (in diesem Fall die 32 Bits eines int), und macht keine Annahmen über die Implementierung von sub hashcode() dafür.

Das XOR hat nur die gleiche Eigenschaft, wenn der Hashcode von B und C es hat, sonst wird es nur das Minimum der Anzahl von "nützlichen" Bits in B- und C-Hashcode verwenden, was zu einer schlechteren Verteilung und häufigeren Kollision führen könnte . Es ist sehr einfach, das Problem zu sehen, wenn B und C Ganzzahlen sind, die dazu neigen, sehr klein zu sein, Sie werden immer nur die ersten paar Bits verwenden (da int.hashcode() die Identitätsfunktion ist).

5
hinzugefügt

Die SUM stellt sicher, dass Sie alle Bits des Hashcodes verwenden, um Ihr Hashing zu verteilen (in diesem Fall die 32 Bits eines int), und macht keine Annahmen über die Implementierung von sub hashcode() dafür.

Das XOR hat nur die gleiche Eigenschaft, wenn der Hashcode von B und C es hat, sonst wird es nur das Minimum der Anzahl von "nützlichen" Bits in B- und C-Hashcode verwenden, was zu einer schlechteren Verteilung und häufigeren Kollision führen könnte . Es ist sehr einfach, das Problem zu sehen, wenn B und C Ganzzahlen sind, die dazu neigen, sehr klein zu sein, Sie werden immer nur die ersten paar Bits verwenden (da int.hashcode() die Identitätsfunktion ist).

5
hinzugefügt

Die SUM stellt sicher, dass Sie alle Bits des Hashcodes verwenden, um Ihr Hashing zu verteilen (in diesem Fall die 32 Bits eines int), und macht keine Annahmen über die Implementierung von sub hashcode() dafür.

Das XOR hat nur die gleiche Eigenschaft, wenn der Hashcode von B und C es hat, sonst wird es nur das Minimum der Anzahl von "nützlichen" Bits in B- und C-Hashcode verwenden, was zu einer schlechteren Verteilung und häufigeren Kollision führen könnte . Es ist sehr einfach, das Problem zu sehen, wenn B und C Ganzzahlen sind, die dazu neigen, sehr klein zu sein, Sie werden immer nur die ersten paar Bits verwenden (da int.hashcode() die Identitätsfunktion ist).

5
hinzugefügt

Die SUM stellt sicher, dass Sie alle Bits des Hashcodes verwenden, um Ihr Hashing zu verteilen (in diesem Fall die 32 Bits eines int), und macht keine Annahmen über die Implementierung von sub hashcode() dafür.

Das XOR hat nur die gleiche Eigenschaft, wenn der Hashcode von B und C es hat, sonst wird es nur das Minimum der Anzahl von "nützlichen" Bits in B- und C-Hashcode verwenden, was zu einer schlechteren Verteilung und häufigeren Kollision führen könnte . Es ist sehr einfach, das Problem zu sehen, wenn B und C Ganzzahlen sind, die dazu neigen, sehr klein zu sein, Sie werden immer nur die ersten paar Bits verwenden (da int.hashcode() die Identitätsfunktion ist).

5
hinzugefügt

Die Antwort lautet (wie immer): " Kommt drauf an. " Es kommt auf eure Klasse an.

Zum Beispiel, wenn Sie darüber nachdenken

class X {
    T a, b;
    X(T _a, _b) { a = _a; b = _b }
}

Sie würden keinen symmetrischen Operator wie + , * oder ^ verwenden (Imagine T ist int und Hashing X (1,2) und X (2,1) Offensichtlich sollte der Hash-Code anders sein, also der erste der drei "Lösungen" (xor oder Hash-Werte) wären schlecht).

Wenn T ein komplexer Typ ist, wäre die dritte Lösung ( Objects.hash() ) möglicherweise ebenfalls schlecht, da nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche Hashwerte zurückgeben) Codes).

1
hinzugefügt
Im Allgemeinen sind nur Objekte, die die HashCode-Standardimplementierung verwenden, für Identitätshashing zuständig. Solche Objekte liegen außerhalb der Reichweite dieser Frage.
hinzugefügt der Autor Basilevs, Quelle
1. Missbrauch des Begriffs "komplexer Typ" (der in CS keine formale Definition hat und sich beispielsweise auf eine komplexe Zahl beziehen kann) 2. implizierte Verletzung von hashCode() + equals() Vertrag Wo fehlt mein Verständnis?
hinzugefügt der Autor Basilevs, Quelle
Was ist ein komplexer Typ? Warum würde ein gleiches Objekt einen anderen Hashcode erzeugen?
hinzugefügt der Autor Basilevs, Quelle
Würde "Composite Type" hier besser funktionieren?
hinzugefügt der Autor Basilevs, Quelle
3. Objects.shash() enthält nur Referenzen für Arrays, da in Ihrem Beispiel keine Arrays vorhanden sind. Dieses Argument ist nicht anwendbar.
hinzugefügt der Autor Basilevs, Quelle
Am allermeisten, " Wenn T ein komplexer Typ ist, wäre die dritte Lösung (Objects.hash ()) möglicherweise auch schlecht, weil nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche zurückgeben) Hash-Codes). "sagt alles: Gleiche Objekte können verschiedene Referenzen haben, die Objects.hash (...) berücksichtigt. Wenn also gleiche Objekte mit unterschiedlichen Referenzen übergeben werden, können sich unterschiedliche Hash-Codes ergeben. Das habe ich geschrieben, und ich denke, es ist richtig.
hinzugefügt der Autor U. Windl, Quelle
Für mich, besonders wenn man eine inkonsistente Sprache wie Java diskutiert, ist das wie Haare spalten: Ob Atomic oder intrinsic_ oder primitiv , es ist alles ein Teil, während komplex , composite ist der andere. In Eiffel gibt es nur erweiterte Typen und Referenz -Typen. Und es gibt sehr klare Verträge über Gleichheit und Hash-Code, die es in Java nicht gibt (und ich glaube, das ist der Grund für das meiste Chaos in Java).
hinzugefügt der Autor U. Windl, Quelle
@Basilevs: Ein -Komplex -Typ ist offensichtlich ein nicht-primitiver Typ, d. H. Ein echter -Referenztyp . Ich weiß nicht, warum du das runtergeholt hast, wenn du nicht verstehst, was ich geschrieben habe.
hinzugefügt der Autor U. Windl, Quelle

Die Antwort lautet (wie immer): " Kommt drauf an. " Es kommt auf eure Klasse an.

Zum Beispiel, wenn Sie darüber nachdenken

class X {
    T a, b;
    X(T _a, _b) { a = _a; b = _b }
}

Sie würden keinen symmetrischen Operator wie + , * oder ^ verwenden (Imagine T ist int und Hashing X (1,2) und X (2,1) Offensichtlich sollte der Hash-Code anders sein, also der erste der drei "Lösungen" (xor oder Hash-Werte) wären schlecht).

Wenn T ein komplexer Typ ist, wäre die dritte Lösung ( Objects.hash() ) möglicherweise ebenfalls schlecht, da nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche Hashwerte zurückgeben) Codes).

1
hinzugefügt
Was ist ein komplexer Typ? Warum würde ein gleiches Objekt einen anderen Hashcode erzeugen?
hinzugefügt der Autor Basilevs, Quelle
3. Objects.shash() enthält nur Referenzen für Arrays, da in Ihrem Beispiel keine Arrays vorhanden sind. Dieses Argument ist nicht anwendbar.
hinzugefügt der Autor Basilevs, Quelle
1. Missbrauch des Begriffs "komplexer Typ" (der in CS keine formale Definition hat und sich beispielsweise auf eine komplexe Zahl beziehen kann) 2. implizierte Verletzung von hashCode() + equals() Vertrag Wo fehlt mein Verständnis?
hinzugefügt der Autor Basilevs, Quelle
Würde "Composite Type" hier besser funktionieren?
hinzugefügt der Autor Basilevs, Quelle
Im Allgemeinen sind nur Objekte, die die HashCode-Standardimplementierung verwenden, für Identitätshashing zuständig. Solche Objekte liegen außerhalb der Reichweite dieser Frage.
hinzugefügt der Autor Basilevs, Quelle
Am allermeisten, " Wenn T ein komplexer Typ ist, wäre die dritte Lösung (Objects.hash ()) möglicherweise auch schlecht, weil nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche zurückgeben) Hash-Codes). "sagt alles: Gleiche Objekte können verschiedene Referenzen haben, die Objects.hash (...) berücksichtigt. Wenn also gleiche Objekte mit unterschiedlichen Referenzen übergeben werden, können sich unterschiedliche Hash-Codes ergeben. Das habe ich geschrieben, und ich denke, es ist richtig.
hinzugefügt der Autor U. Windl, Quelle
Für mich, besonders wenn man eine inkonsistente Sprache wie Java diskutiert, ist das wie Haare spalten: Ob Atomic oder intrinsic_ oder primitiv , es ist alles ein Teil, während komplex , composite ist der andere. In Eiffel gibt es nur erweiterte Typen und Referenz -Typen. Und es gibt sehr klare Verträge über Gleichheit und Hash-Code, die es in Java nicht gibt (und ich glaube, das ist der Grund für das meiste Chaos in Java).
hinzugefügt der Autor U. Windl, Quelle
@Basilevs: Ein -Komplex -Typ ist offensichtlich ein nicht-primitiver Typ, d. H. Ein echter -Referenztyp . Ich weiß nicht, warum du das runtergeholt hast, wenn du nicht verstehst, was ich geschrieben habe.
hinzugefügt der Autor U. Windl, Quelle

Die Antwort lautet (wie immer): " Kommt drauf an. " Es kommt auf eure Klasse an.

Zum Beispiel, wenn Sie darüber nachdenken

class X {
    T a, b;
    X(T _a, _b) { a = _a; b = _b }
}

Sie würden keinen symmetrischen Operator wie + , * oder ^ verwenden (Imagine T ist int und Hashing X (1,2) und X (2,1) Offensichtlich sollte der Hash-Code anders sein, also der erste der drei "Lösungen" (xor oder Hash-Werte) wären schlecht).

Wenn T ein komplexer Typ ist, wäre die dritte Lösung ( Objects.hash() ) möglicherweise ebenfalls schlecht, da nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche Hashwerte zurückgeben) Codes).

1
hinzugefügt
Was ist ein komplexer Typ? Warum würde ein gleiches Objekt einen anderen Hashcode erzeugen?
hinzugefügt der Autor Basilevs, Quelle
Im Allgemeinen sind nur Objekte, die die HashCode-Standardimplementierung verwenden, für Identitätshashing zuständig. Solche Objekte liegen außerhalb der Reichweite dieser Frage.
hinzugefügt der Autor Basilevs, Quelle
1. Missbrauch des Begriffs "komplexer Typ" (der in CS keine formale Definition hat und sich beispielsweise auf eine komplexe Zahl beziehen kann) 2. implizierte Verletzung von hashCode() + equals() Vertrag Wo fehlt mein Verständnis?
hinzugefügt der Autor Basilevs, Quelle
Würde "Composite Type" hier besser funktionieren?
hinzugefügt der Autor Basilevs, Quelle
3. Objects.shash() enthält nur Referenzen für Arrays, da in Ihrem Beispiel keine Arrays vorhanden sind. Dieses Argument ist nicht anwendbar.
hinzugefügt der Autor Basilevs, Quelle
Für mich, besonders wenn man eine inkonsistente Sprache wie Java diskutiert, ist das wie Haare spalten: Ob Atomic oder intrinsic_ oder primitiv , es ist alles ein Teil, während komplex , composite ist der andere. In Eiffel gibt es nur erweiterte Typen und Referenz -Typen. Und es gibt sehr klare Verträge über Gleichheit und Hash-Code, die es in Java nicht gibt (und ich glaube, das ist der Grund für das meiste Chaos in Java).
hinzugefügt der Autor U. Windl, Quelle
Am allermeisten, " Wenn T ein komplexer Typ ist, wäre die dritte Lösung (Objects.hash ()) möglicherweise auch schlecht, weil nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche zurückgeben) Hash-Codes). "sagt alles: Gleiche Objekte können verschiedene Referenzen haben, die Objects.hash (...) berücksichtigt. Wenn also gleiche Objekte mit unterschiedlichen Referenzen übergeben werden, können sich unterschiedliche Hash-Codes ergeben. Das habe ich geschrieben, und ich denke, es ist richtig.
hinzugefügt der Autor U. Windl, Quelle
@Basilevs: Ein -Komplex -Typ ist offensichtlich ein nicht-primitiver Typ, d. H. Ein echter -Referenztyp . Ich weiß nicht, warum du das runtergeholt hast, wenn du nicht verstehst, was ich geschrieben habe.
hinzugefügt der Autor U. Windl, Quelle

Die Antwort lautet (wie immer): " Kommt drauf an. " Es kommt auf eure Klasse an.

Zum Beispiel, wenn Sie darüber nachdenken

class X {
    T a, b;
    X(T _a, _b) { a = _a; b = _b }
}

Sie würden keinen symmetrischen Operator wie + , * oder ^ verwenden (Imagine T ist int und Hashing X (1,2) und X (2,1) Offensichtlich sollte der Hash-Code anders sein, also der erste der drei "Lösungen" (xor oder Hash-Werte) wären schlecht).

Wenn T ein komplexer Typ ist, wäre die dritte Lösung ( Objects.hash() ) möglicherweise ebenfalls schlecht, da nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche Hashwerte zurückgeben) Codes).

1
hinzugefügt
1. Missbrauch des Begriffs "komplexer Typ" (der in CS keine formale Definition hat und sich beispielsweise auf eine komplexe Zahl beziehen kann) 2. implizierte Verletzung von hashCode() + equals() Vertrag Wo fehlt mein Verständnis?
hinzugefügt der Autor Basilevs, Quelle
Was ist ein komplexer Typ? Warum würde ein gleiches Objekt einen anderen Hashcode erzeugen?
hinzugefügt der Autor Basilevs, Quelle
Im Allgemeinen sind nur Objekte, die die HashCode-Standardimplementierung verwenden, für Identitätshashing zuständig. Solche Objekte liegen außerhalb der Reichweite dieser Frage.
hinzugefügt der Autor Basilevs, Quelle
Würde "Composite Type" hier besser funktionieren?
hinzugefügt der Autor Basilevs, Quelle
3. Objects.shash() enthält nur Referenzen für Arrays, da in Ihrem Beispiel keine Arrays vorhanden sind. Dieses Argument ist nicht anwendbar.
hinzugefügt der Autor Basilevs, Quelle
Am allermeisten, " Wenn T ein komplexer Typ ist, wäre die dritte Lösung (Objects.hash ()) möglicherweise auch schlecht, weil nur die Referenzen berücksichtigt werden (gleiche Objekte könnten unterschiedliche zurückgeben) Hash-Codes). "sagt alles: Gleiche Objekte können verschiedene Referenzen haben, die Objects.hash (...) berücksichtigt. Wenn also gleiche Objekte mit unterschiedlichen Referenzen übergeben werden, können sich unterschiedliche Hash-Codes ergeben. Das habe ich geschrieben, und ich denke, es ist richtig.
hinzugefügt der Autor U. Windl, Quelle
Für mich, besonders wenn man eine inkonsistente Sprache wie Java diskutiert, ist das wie Haare spalten: Ob Atomic oder intrinsic_ oder primitiv , es ist alles ein Teil, während komplex , composite ist der andere. In Eiffel gibt es nur erweiterte Typen und Referenz -Typen. Und es gibt sehr klare Verträge über Gleichheit und Hash-Code, die es in Java nicht gibt (und ich glaube, das ist der Grund für das meiste Chaos in Java).
hinzugefügt der Autor U. Windl, Quelle
@Basilevs: Ein -Komplex -Typ ist offensichtlich ein nicht-primitiver Typ, d. H. Ein echter -Referenztyp . Ich weiß nicht, warum du das runtergeholt hast, wenn du nicht verstehst, was ich geschrieben habe.
hinzugefügt der Autor U. Windl, Quelle

Dies liegt daran, dass sum für eine bessere Verteilung sorgt als xor .

Zum Beispiel, wenn int a und b Werte zwischen 0 und 7 haben ( 000 und 111 binary), dann ist das Ergebnis von xor dieser beiden Argumente immer zwischen 0 und 7 (da xor nur 3 Bits ändert). Wenn Sie jetzt eine Multiplikation und eine Summe durchführen, werden Sie eine viel bessere Verteilung haben, da die Werte nicht innerhalb des Bereichs von 0 und 7 liegen.

0
hinzugefügt
Übrigens ist int hashCode sein Wert? Es wäre sehr schlecht für nicht-einheitliche Verteilungen für die meisten Anwendungsfälle, was für HashMap und andere Hash-basierte Algorithmen schlecht ist.
hinzugefügt der Autor Basilevs, Quelle
Hängt von der Umsetzung ab ^ aber die Antwort ist leider oft ja.
hinzugefügt der Autor C4stor, Quelle
@ Basilevs Ja, ich meinte breiter, besser, reparierte die Antwort, danke.
hinzugefügt der Autor Adam Siemion, Quelle

Dies liegt daran, dass sum für eine bessere Verteilung sorgt als xor .

Zum Beispiel, wenn int a und b Werte zwischen 0 und 7 haben ( 000 und 111 binary), dann ist das Ergebnis von xor dieser beiden Argumente immer zwischen 0 und 7 (da xor nur 3 Bits ändert). Wenn Sie jetzt eine Multiplikation und eine Summe durchführen, werden Sie eine viel bessere Verteilung haben, da die Werte nicht innerhalb des Bereichs von 0 und 7 liegen.

0
hinzugefügt
Übrigens ist int hashCode sein Wert? Es wäre sehr schlecht für nicht-einheitliche Verteilungen für die meisten Anwendungsfälle, was für HashMap und andere Hash-basierte Algorithmen schlecht ist.
hinzugefügt der Autor Basilevs, Quelle
Hängt von der Umsetzung ab ^ aber die Antwort ist leider oft ja.
hinzugefügt der Autor C4stor, Quelle
@ Basilevs Ja, ich meinte breiter, besser, reparierte die Antwort, danke.
hinzugefügt der Autor Adam Siemion, Quelle

Dies liegt daran, dass sum für eine bessere Verteilung sorgt als xor .

Zum Beispiel, wenn int a und b Werte zwischen 0 und 7 haben ( 000 und 111 binary), dann ist das Ergebnis von xor dieser beiden Argumente immer zwischen 0 und 7 (da xor nur 3 Bits ändert). Wenn Sie jetzt eine Multiplikation und eine Summe durchführen, werden Sie eine viel bessere Verteilung haben, da die Werte nicht innerhalb des Bereichs von 0 und 7 liegen.

0
hinzugefügt
Übrigens ist int hashCode sein Wert? Es wäre sehr schlecht für nicht-einheitliche Verteilungen für die meisten Anwendungsfälle, was für HashMap und andere Hash-basierte Algorithmen schlecht ist.
hinzugefügt der Autor Basilevs, Quelle
Hängt von der Umsetzung ab ^ aber die Antwort ist leider oft ja.
hinzugefügt der Autor C4stor, Quelle
@ Basilevs Ja, ich meinte breiter, besser, reparierte die Antwort, danke.
hinzugefügt der Autor Adam Siemion, Quelle

Dies liegt daran, dass sum für eine bessere Verteilung sorgt als xor .

Zum Beispiel, wenn int a und b Werte zwischen 0 und 7 haben ( 000 und 111 binary), dann ist das Ergebnis von xor dieser beiden Argumente immer zwischen 0 und 7 (da xor nur 3 Bits ändert). Wenn Sie jetzt eine Multiplikation und eine Summe durchführen, werden Sie eine viel bessere Verteilung haben, da die Werte nicht innerhalb des Bereichs von 0 und 7 liegen.

0
hinzugefügt
Übrigens ist int hashCode sein Wert? Es wäre sehr schlecht für nicht-einheitliche Verteilungen für die meisten Anwendungsfälle, was für HashMap und andere Hash-basierte Algorithmen schlecht ist.
hinzugefügt der Autor Basilevs, Quelle
Hängt von der Umsetzung ab ^ aber die Antwort ist leider oft ja.
hinzugefügt der Autor C4stor, Quelle
@ Basilevs Ja, ich meinte breiter, besser, reparierte die Antwort, danke.
hinzugefügt der Autor Adam Siemion, Quelle