Methoden hashCode() & equals()

Bitte aktiviere JavaScript!
Ich bin gerade dabei zu recherchieren wie sich hashCode() und equals() in z.B. einer HashMap verhalten.
Habe eine Sache bei der ich gerne wissen würde ob diese von mir richtig verstanden wurde.

-Die hashCode() Methode sollte nie einen fixen Wert zurück geben, da Objekte dann immer an der selben Stelle im Bucket gespeichert werden
und der Performance Vorteil für Zugriff, Suche und Einfügen verloren geht. Die O(1) Konstanten Zeiten werden damit zu O(n) linearen Zeiten. (O(n) bedeutet mit zunehmenden Objekten sinkt die Performance linear.

Das Überschreiben der equals() Methode bereitet mir ein wenig mehr Kopfschmerzen.

-Warum muss die equals() Methode aus der Object Klasse überschrieben werden?
-Was für einen Vorteil habe ich wenn meine Implementierung wie folgt aussieht?

Java:
public class User {

    private long id;
    private String name;
    private String email;

    //Kontruktoren, Getter, Setter

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null)
            return false;
        if (this.getClass() != o.getClass())
            return false;
        User user = (User) o;
        return id == user.id && (name.equals(user.name) && email.equals(user.email));
    }
}
 
-Die hashCode() Methode sollte nie einen fixen Wert zurück geben, da Objekte dann immer an der selben Stelle im Bucket gespeichert werden
und der Performance Vorteil für Zugriff, Suche und Einfügen verloren geht. Die O(1) Konstanten Zeiten werden damit zu O(n) linearen Zeiten. (O(n) bedeutet mit zunehmenden Objekten sinkt die Performance linear.
In Bezug auf HashMap passt das.


-Warum muss die equals() Methode aus der Object Klasse überschrieben werden?
Weil diese Methode zum vergleichen von zwei Objekten benutzt wird.

-Was für einen Vorteil habe ich wenn meine Implementierung wie folgt aussieht?
Vorteil gegenüber was?

Vorteil gegenüber nicht überschriebener equals-Methode ist das Vorhandensein einer sinnvollen equals-Methode. Jetzt sind zwei User gleich, wenn ihre Attribute gleich sind, ohne wäre das nicht der Fall.
 
Mit equals erfolgt die Prüfung auf Gleichheit. Wenn Du diese nicht überschreibst, bleibt nur die Überprüfung, ob die Referenz gleich ist, aber das reicht nicht aus, sobald die Werte z.B. irgendwo gelesen werden. Und Du willst ja, dass zwei Instanzen mit den gleichen Werten, auch als gleich erkannt wird.

Die Implementation ist so ok. Generell muss man halt alle Fälle abdecken:
- null Wert übergeben
- Instanz einer anderen Klasse übergeben
- korrekte Instanz übergeben

Was mit aber bei Dir auffällt: Du rufst direkt auf name und email das equals auf. Wenn name oder email aber null ist, dann bekommst Du eine NPE.
Ich prüfe daher Strings generell mit Konstrukten wie:
(name != null ? name.equals(user.name) : user.name == null)
oder ich nutze eine Library, die mir z.B. eine statische Funktion equals(String, String) bietet (z.B. Apache commons StringUtils Klasse)
 
@friednoodles Und auch das hier
Java:
        if (this == o)
            return true;
        if (o == null)
            return false;
        if (this.getClass() != o.getClass())
            return false;
lässt sich schöner schreiben
Java:
        if (o == null || o == this || getClass() != o.getClass()) 
            return o == this;
 
@friednoodles Und auch das hier
Java:
        if (this == o)
            return true;
        if (o == null)
            return false;
        if (this.getClass() != o.getClass())
            return false;
lässt sich schöner schreiben
Java:
        if (o == null || o == this || getClass() != o.getClass())
            return o == this;
Wobei Schönheit da im Auge des Betrachters liegt :p

Den Test auf this würd ich einzeln lassen, ist mMn einfacher lesbar und verständlicher.
 
Da würde mich einmal interessieren, in wie weit ihr denn sowas noch manuell schreibt oder ob ihr Lombok nutzt (oder etwas ähnliche, falls es da noch mehr Lösungen für gibt - ich kenne da erst einmal nur Lombok).

Java ist da halt sehr viel, was ich immer gerne als "EDV zu Fuss" bezeichne. Diese ganzen Getter und Setter zu schreiben ist halt einfach nur Tipparbeit. (Ich bin da von .Net mit den Properties dort halt anderes gewohnt. Und Kotlin, das ich einige Zeit genutzt habe, hat diesbezüglich auch keine "Probleme".)
 
Da würde mich einmal interessieren, in wie weit ihr denn sowas noch manuell schreibt oder ob ihr Lombok nutzt (oder etwas ähnliche, falls es da noch mehr Lösungen für gibt - ich kenne da erst einmal nur Lombok).
Bisher meist alles händisch, sowohl equals (nach Bloch-Template) als auch Get/Setter - wobei händisch dabei IDE-generiert ist.

Ich bin allerdings sparsam mit Gettern und noch mehr mit Settern, und equals braucht meist auch nur die ID oder ein paar wenige Felder.
 
Danke für die bisherigen Antworten. Habe das alles soweit verstanden, aber würde gerne noch etwas Fragen bevor ich dafür extra einen neues Thema aufmache.

Kann man den Vorteil von Hashing gegenüber anderer Datenstrukturen in den konstant schnellen Operationen sehen?

Wie wird wenn ich die containsKey() Methode auf eine HashMap anwende der gesuchte key gefunden?
Ich habe in den vielen Tutorials bisher irgendwo einmal gelesen das der Hash vom ersten Entry generiert wird und von dort aus dann direkt ein Sprung zum gesuchten Element stattfindet. Stimmt das?
 
Das Überschreiben der equals() Methode bereitet mir ein wenig mehr Kopfschmerzen.

-Warum muss die equals() Methode aus der Object Klasse überschrieben werden?
-Was für einen Vorteil habe ich wenn meine Implementierung wie folgt aussieht?
Wenn du equals nicht überschreibst wird geprüft ob sie die gleichen sind und nicht ob so gleich sind.
Unterschied klar?
Java:
public class User
{
    private long id;
    private String name;
    
    public User(long id, String name)
    {
        this.id = id;
        this.name = name;
    }

    public static void main(String[] args)
    {
        User u1 = new User(1, "Test");
        User u2 = new User(1, "Test");
        System.out.println(u1.equals(u2));
    }
}
false, das sind nicht die gleichen instanzen.
Was ist gleich? Das hängt im Grunde von der Fachlichkeit der Anwendung ab..

so sind sie gleich:
Java:
import java.util.Objects;

public class User
{
    private long id;
    private String name;

    public User(long id, String name)
    {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        User user = (User) o;
        return id == user.id &&
            Objects.equals(name, user.name);
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(id, name);
    }

    public static void main(String[] args)
    {
        User u1 = new User(1, "Test");
        User u2 = new User(1, "Test");
        System.out.println(u1.equals(u2));
    }
}
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben