Was bringen sich Hash-Code und Equals Methode Java?

3 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Mittels der hashCode-Methode wird für ein Objekt ein Hashwert berechnet, den Datenstrukturen wie die HashMap oder das HashSet verwenden, um zu entscheiden, wo sie das Objekt, welches ihnen zugefügt werden soll, bei sich intern ablegen.

Stell dir dazu ein Array vor:

-----------------
|   |   |   |   |
-----------------

In jede Datenzelle können mehrere Objekte abgelegt werden. Objekte mit dem Hashwert 2 würden beispielsweise bei Index 1 eingefügt werden, Objekte mit dem Hashwert 3 bei Index 2, usw.. Wenn der Hashwert größer als die Länge des Arrays ist, wird es entweder vergrößert (das wäre allerdings ein sehr teurer Prozess) oder aber die Zählung für den Index startet wieder bei 0 (ein Objekt mit dem Hashwert 5 würde also in Index 2 wandern).

Wenn mehrere Objekte in eine Zelle gelagert werden, redet man von Kollisionen. Kollisionen sind ungünstig für den Lesevorgang, denn um das richtige Objekt zu ermitteln, müssen erst alle Objekte in der betreffenden Zelle durchlaufen werden. Umso mehr Kollisionen es gibt, umso länger dauert es.

Das Ziel für eine Hashfunktion, die so einen Hashwert berechnet, wäre es also, eine breite Verteilung zu ermöglichen, sodass jede Zelle möglichst wenige Objekte beinhaltet. Eine Funktion, bei der die Objekte immer nur einen statischen Wert (z.B. 1) zurückgeben, wäre bspw. schlecht, da somit alle Objekte in die erste Zelle wandern würden. Etwas besser wäre eine Funktion, bei der die Zellen nach einer Zahlenfolge wie 2, 4, 6, 8, ... befüllt würden. Jedes Objekt sollte also einen individuellen Hashwert liefern, der dafür sorgt, dass nicht immer die gleichen Zellen befüllt werden.

Für gute hashCode-Implementationen hat Java noch weitere Kriterien definiert. So sollte sich der Hashwert eines Objekts nur ändern, wenn es eine Zustandsänderung gab und gleiche Objekte (also Objekte, bei denen bei Vergleich via equals das Ergebnis true geliefert wird) müssen den selben Hashwert besitzen.

Mit der equals-Methode wiederum kannst du Objekte vergleichen. Bei bestimmten Klassen (wie String) sorgt die equals-Implementation dafür, dass ein Vergleich nach Wert durchgeführt wird. Die Standardimplementation der Object-Klasse wiederum nutzt den Vergleichsoperator ==, was also für einen Vergleich nach Identität sorgt (Liegen beide Vergleichsobjekte in der selben Speicherzelle?).

Du selbst kannst die Methode für eine Klasse ebenfalls überschreiben, um selbst vorzugeben, wann ein Objekt dieser Klasse verglichen mit einem anderen Objekt als gleich gewertet werden soll.

Beispiel:

class Dog {
  @Override
  public boolean equals(Object other) {
    return true;
  }
}

class Cat {
}

// main:
var dog = new Dog();
var cat = new Cat();

if (dog.equals(cat)) {
  System.out.println("They are the same.");
}

Dieses Programm würde dir nun bescheinigen, dass Hund und Katze gleich sind. Im Grunde würde ein Dog-Objekt nun verglichen mit jedem Objekt als gleich gewertet werden, so lange man den Spieß nicht umdreht:

if (cat.equals(dog)) {
  System.out.println("They are the same.");
}

In Java wurden daher ein paar Bedingungen definiert, die eine equals-Methode erfüllen sollte:

  1. Wenn man ein Objekt mit sich selbst vergleicht, sollte es als gleich gewertet werden.
  2. Das Ergebnis von equals sollte sich nicht mit jedem Aufruf ändern, sofern keine zwischenzeitliche Zustandsänderung der Objekte erfolgt. Das heißt, bei jedem equals-Aufruf einen Zufallsgenerator für das Ergebnis anzuwerfen, wäre ein Verstoß der Bedingung.
  3. Egal, ob man nun Objekt A mit Objekt B vergleicht oder Objekt B mit Objekt A - das Ergebnis sollte stets gleich sein. Das heißt, meine obige Beispielimplementation würde diese Regel bereits brechen.
  4. Angenommen, es gibt zwei gleichwertige Objekte A und B. Dann sollte ein Objekt C, welches als gleichwertig zu B bewertet wird, auch gleich zu A sein.

Sobald du die equals-Methode für einen Typ überschreibst, solltest du auch die hashCode-Methode überschreiben (und dabei die genannten Kriterien beachten). Andernfalls kannst du auf folgende Probleme stoßen:

  • Das HashSet wird mit doppelten Werten befüllt (da gleichwertige Objekte bei Nutzung der Standardimplementation von hashCode in unterschiedliche Zellen gespeichert werden)
  • Objekte gleichen Werts sind im HashSet nicht auffindbar, da die Standardimplementation von equals nach identischen Objekten schaut und nicht nur Wertgleichheit.
Hat es eine Auswirkung auf ein Objekt HashSet, wenn bei der hashcode und equals-Methode von der Java-util Klasse HashSet etwas verändert wird?

Das kommt auf die Änderung an. Wenn du die Logik änderst, würden HashSet-Objekte (wohlgemerkt: Es geht um das HashSet-Objekt selbst, nicht um dessen gespeicherten Objekte) andere Hashwerte liefern und beim Vergleich mit anderen Objekten könnten andere Ergebnisse als zuvor zurückgegeben werden. Vielleicht wäre ein HashSet-Objekt dann gleichwertig zu einem Objekt String, wenn du es so definierst?

Genauso wäre es auch bei meinem Beispiel oben. Wenn ich dort die equals-Implementation ändere, ist der Hund vielleicht nicht mehr gleichwertig zu einer Katze (oder überhaupt irgendeinem Objekt).

class Dog {
  @Override
  public boolean equals(Object other) {
    return false;
  }
}

Dies wäre soweit jedenfalls der Fall, wenn die Änderungen statisch (also vor Kompilierung bzw. zur Entwicklungszeit) vorgenommen werden würden. Bei einer Logikänderung zur Laufzeit (bspw. via Reflection) wäre es kniffliger, denn man müsste dafür sorgen, dass alle (also ebenso bereits bestehende) Objekte überhaupt die neue Implementation verwenden.

LukasErdi 
Fragesteller
 01.12.2021, 22:20

danke :)

0

Hey,

mit der equals() Methode kannst du zwei Objekte miteinander vergleichen. Diese returned true, wenn die Objekte gleich sind (auch Strings werden so verglichen, bitte nicht == nutzen).

Die hashCode() Methode returnt dir einen hash spezifisch zu diesem Objekt. Was genau ein Hash ist kannst du einfach googlen.

Mfg Jannick (L1nd)

Woher ich das weiß:Hobby
LukasErdi 
Fragesteller
 24.11.2021, 15:24

Ich weiß aber ändert sich was dann spezifisch, wenn man von der Klasse nach dem Überschreiben ein Objekt erstellt, also ob dann das Objekt anders ist (und zwar in dem Fall von der Klasse HashSet)?

0
JanMarcel01  24.11.2021, 15:39
@LukasErdi

Die hashCode() Methode hat keinen Einfluss auf das HashSet. Da das HashSet nicht statisch ist, hat jede neue Instanz deiner Klasse auch eine neue Instanz des HashSet

0

Meines Erachtens nach bringen sich diese methoden sehr viel, unter anderem bin ein sehr guter Programmierer das bedeutet du kannst mir vertrauen.

BlueJ ist auch ein sehr tolles Programm wo man vieles ausprobieren kann und mit dem arbeitet zb. auch Bill Gates

LukasErdi 
Fragesteller
 24.11.2021, 15:02

aha xd

0