equals Methode möglichst effizient

ocsme

Top Contributor
Guten Tag zusammen,

ich befasse mich derzeit etwas mit dem Collection-Framework von Java. Dabei wird ja nur mit Referenztypen gearbeitet und um diese zu vergleichen benötigt man ja die von Object ererbte Methode equals 🙂

Nun wollte ich mal nachfragen ob es eine effizienten Weg gibt diese equals Methode zu schreiben. Wir haben früher immer sämtliche Primitivien Typen mittels "==" auf Gleichheit überprüft und für Referenztypen dann eine equals Methode. Doch je mehr Referenztypen in einer Klasse stehen desto größer muss eine solche equals Methode werden.

Unser Prof meinte wir sollen uns eine eigene hashCode() Methode schreiben die einen eindeutigen Wert für jedes Objekt zurück gibt und man so dann eben auf Gleichheit Prüfen kann 🙂 alles schön und gut doch ich hab keine Idee wie ich mir so eine hashCode() Methode schreiben soll 😛
Hab mir zu diesem Thema auch schon einiges angeschaut und auch sowas haben wir in ALDA bearbeitet doch was wäre wenn für ein verschiedenes Objekt dann der selbe Hash raus kommt?

Hat jemand eine Idee? oder sprengt das Thema den rahmen ?

LG
 
doch was wäre wenn für ein verschiedenes Objekt dann der selbe Hash raus kommt?
Dann ist dein Hashcode Methode einfach nicht sauber implementiert. Der "Vertrag" zwischen equals(Object) und hashcode() besagt, dass wenn 2 Objekte durch die equals(Object) Methode als gleich identifiziert werden, muss die hashcode() Methode den selben Integerwert zurückliefern.
Das ist an sich auch nicht so schwer. Seit Java 7 (glaub ich) gibt es die Objects#hash(Object...) Methode, die für dich die Arbeit macht.
Java:
@Override
public int hashCode() {
    return Objects.hash(attr1, attr2, ... , attrN);
}
 
Unser Prof meinte wir sollen uns eine eigene hashCode() Methode schreiben die einen eindeutigen Wert für jedes Objekt zurück gibt und man so dann eben auf Gleichheit Prüfen kann
Man kann im Allgemeinen mit dem Hash-Code nur die Ungleichheit feststellen, nicht jedoch die Gleichheit, d. h. aus a.equals(b) folgt a.hashCode() == b.hashCode(), die Umkehrung gilt jedoch nicht. Das einzige, was man mit Sicherheit sagen kann (können muss): a.hashCode() != b.hashCode() ==> !a.equals(b)

Dazu musst Du Dir lediglich Strings ansehen: der Hash-Code ist eine 32-Bit-Zahl, hat also eine Breite von 4 Bytes. Damit kann einem 4 Byte langen String genau eine 32-Bit-Zahl zugewiesen werden. Sobald Du ein Byte dazunimmst, muss es unweigerlich zu Kollisionen kommen (können).

Die Hashfunktion sollte natürlich so gewählt werden, dass die Wahrscheinlichkeit, dass im Fall von a.hashCode() == b.hashCode() die Gleichheit von a und b folgt, möglichst hoch ist (sprich: die Zahl der Kollisionen soll möglichst gering sein).
 
Also das bedeutet nun:
Java nimmt mir die Arbeit ab mir eine Hash Funktion einfallen zu lassen oder eine zu Implementieren die es so gibt.
Die Hash Funktion Prüft jedoch nur ob die Objekte ungleich sind.
Das bedeutet weiterhin ich benötige trotzdem eine equals Methode und sobald ich mehrere Objekte habe dann eben auch immer tiefere equals Methoden 🙁
das gefällt mir jetzt aber überhaupt nicht 🙁
Keine andere Alternative 😀 ???

Aber schon mal gut das hash nicht explizit gecodet werden muss denn daran wäre ich ja jetzt schon gescheitert da ich keine dieser Funktionen kenne und ich mich damit noch gar nicht auseinander gesetzt habe 😀

Der "Vertrag" zwischen equals(Object) und hashcode() besagt, dass wenn 2 Objekte durch die equals(Object) Methode als gleich identifiziert werden, muss die hashcode() Methode den selben Integerwert zurückliefern.
Ja das habe ich auch vorhin gelesen 😀
Gleichheit von Schlüsseln:

  • HashMap ruft auch die equals Methode auf um Gleichheit von Schlüsselobjekte festzustellen

  • Weiter verwenden viele Klassen des Collection-Frameworks die Methode hashCode

  • Dabei ist es unerlässlich das zwei Schlüsselobjekte den gleichen Hashcode liefern wenn sie laut equals gleich sind

  • equals und hashCode müssen also unbedingt im Einklang stehen

Wieso kann man in der Methode:
Java:
public static int hash(Object... values)
Ein Obejct varargs rein stecken?
 
Zuletzt bearbeitet:
Aus der Javadoc der Methode:

"
This method is useful for implementing Object.hashCode() on objects containing multiple fields. For example, if an object that has three fields, x, y, and z, one could write:

@Override public int hashCode() {
return Objects.hash(x, y, z);
}
..."

also wenn du ein Objekt hast dessen Felder wieder Objekte sind kannst du so leicht hashCode für dieses Objekt implementieren.

Ich mache gerade auf Udemy einen Spring-Kurs, und in dem kam vor dass wenn man mit Objekten arbeitet die zum Beispiel über Hibernate persistiert werden hashCode() sowas einfaches wie die generierte id zurückgeben kann. Also hashCode muss nichts kompliziertes sein solange der Code eben eindeutig ist (eindeutiges Mapping zwischen gehashtem Wert und resultierendem hash), IDEs haben (oder zumindest IntelliJ hat) zum Beispiel auch eine Funktionalität um die Implementierung von hashCode auf Basis von ausgewählten Feldern automatisch zu generieren.
 
Die Hash Funktion Prüft jedoch nur ob die Objekte ungleich sind.
Die Hashfunktion prüft gar nichts, sie bildet lediglich ein Objekt auf eine Zahl ab. Der Vertrag von Object verlangt dabei, dass für zwei Objekte, die gem. equals gleich sind, den gleichen Hashcode liefern müssen.

Daraus folgt, dass zwei Objekte, die unterschiedliche Hashcodes liefern, ungleich (gem. equals) sein müssen (denn wären sie gem. equals gleich, müssten sie ja den gleichen Hashcode liefern).

Das bedeutet weiterhin ich benötige trotzdem eine equals Methode und sobald ich mehrere Objekte habe dann eben auch immer tiefere equals Methoden 🙁
das gefällt mir jetzt aber überhaupt nicht
Ja, Du musst equals und hashCode praktisch immer gemeinsam überschreiben. Nur equals oder nur hashCode darf nicht sein, da sonst die von Object geforderten Eigenschaften nicht eingehalten werden.

Mal ein einfaches Beispiel:
Java:
public class Point {
    int x, y;
    public boolean equals(Object o) {
        if (o == null || o == this || !(o instanceof Point)) {
            return o == this;
        }
        Point p = (Point) o;
        return x == p.x && y == p.y;
    }

    public int hashCode() {
        return x;
    }
}
Das wäre eine gültige Implementierung, die allerdings sehr ungünstig ist, weil alle Punkte, die auf der gleichen x-Koordinate liegen, den gleichen Hashcode liefern. Umgekehrt ist klar: falls sich zwei Punkte in den x-Koordinaten unterscheiden, muss es sich um verschiedene Punkte handeln. D. h. liefert hashCode() unterschiedliche Werte, sind auch die Punkte unterschiedlich.

Man kann den Spaß verbessern:
Java:
    public int hashCode() { 
        return x + y;
    }
Jetzt liefern "nur" noch die Punkte, deren Summe aus x- und y-Koordinate gleich ist, den selben Hash-Code. Besser als nix.

Man kann z. B. mit der Cantorschen Paarungsfunktion für ein Zahlenpaar eine eindeutige Zahl (bijektiv) bestimmen. Das wäre in der Theorie das, was Du suchst:
Java:
    public int hashCode() {
        return (x+y)*(x+y+1)/2 + y;
    }
Allerdings macht Dir auch hier der Wertebereich von int einen Strich durch die Rechnung: die Berechnung wird relativ schnell zu einem Überlauf und damit auch zu Kollisionen führen.
 
Danke jetzt bin ich um einiges Schlauer 🙂
Das bedeutet nur immer wieder Arbeit vor allem mit dieser equals Methode.
Denn ich vermute mal wenn man einmal eine halbwegs "Ordentliche" Hashcode Funktion geschrieben hat ist der immer "save". Oder man nimmt eben die von Java 😛
Mein größtes Problem besteht dann noch in der equals Methode :/

Danke nochmals 🙂

LG
 
😵 wie stellt diese Methode denn den Vergleich an ???
So jetzt bin ich wieder verwirrt 🙁

Die Arbeit mit dem paarweisen Vergleich bei Referenztypen besteht weiterhin nicht wahr?
_______________________

Und eine Frage hab ich von deinem Code von oben:
Java:
   if (o == null || o == this || !(o instanceof Point)) {

Wie ich mit null die Methode aufrufe weiß ich, wie ich die Methode mit einem anderen Objekt aufrufe ist auch Klar ABER wie kommt es den zum fall o == this?
equals(this)?

LG
 
Zuletzt bearbeitet:
Der paarweise Vergleich bei Referenztypen nimmt mir aber die Methode: Objects.equals auch nicht ab! oder?
Du meinst alle Paare auf einmal? Nein, das nicht. Object.equals gibt für zwei angegebene Objekte o1 und o2 true zurück, gdw. eine der folgenden Bedingungen erfüllt ist:
1. o1 == null && o2 == null
2. o1 != null && o2 != null && o1.equals(o2)
Es ist also wesentlich schöner
Java:
if (Objects.equals(o1, o2)) {
schreiben zu können statt
Java:
if (o1 == null && o2 == null || o1 != null && o1.equals(o2)) {
...
 
Okay danke nochmals für das Feedback 🙂
Bin froh euch zu Haben 🙂

Jetzt ist erstmal alles gerade gerückt 😀 bis ich die nächsten Seiten meines Kapitels lese und wieder 1.000 Fragen im Kopf habe und nach erfolglosem google euch Frage 😀

LG
 

Neue Themen


Zurück
Oben