Sobes.tech
Middle

Αναφέρετε τη συμφωνία των μεθόδων hashCode και equals στη Java.

sobes.tech AI

Απάντηση από AI

Ο συμβολισμός των μεθόδων hashCode και equals στη Java καθορίζει τη σχέση τους και εγγυάται τη σωστή συμπεριφορά των αντικειμένων κατά τη χρήση συλλογών που βασίζονται σε πίνακες κατακερματισμού (π.χ., HashSet, HashMap).

Οι βασικοί κανόνες του συμβολαίου:

  1. Ανακλαστικότητα: Για οποιαδήποτε μη null αναφορά x, το x.equals(x) πρέπει να επιστρέφει true.
  2. Αμφισημία: Για οποιεσδήποτε μη null αναφορές x και y, το x.equals(y) πρέπει να επιστρέφει true αν και μόνο αν το y.equals(x) επιστρέφει true.
  3. Μεταθετικότητα: Για οποιεσδήποτε μη null αναφορές x, y και z, αν το x.equals(y) και το y.equals(z) είναι true, τότε το x.equals(z) πρέπει να είναι true.
  4. Συνεπτικότητα: Για οποιεσδήποτε μη null αναφορές x και y, η επανειλημμένη κλήση του x.equals(y) με τα ίδια αντικείμενα x και y πρέπει πάντα να επιστρέφει το ίδιο αποτέλεσμα.
  5. Μη null: Για οποιαδήποτε μη null αναφορά x, το x.equals(null) πρέπει να επιστρέφει false.

Και ο βασικός κανόνας που συνδέει το hashCode και το equals:

  • Αν δύο αντικείμενα είναι ίσα σύμφωνα με τη μέθοδο equals(Object), τότε η κλήση της μεθόδου hashCode() για κάθε ένα από αυτά πρέπει να παράγει τον ίδιο ακέραιο αριθμό.
  • Αντίθετα, όχι ισχύει: αν δύο αντικείμενα έχουν τον ίδιο κωδικό κατακερματισμού, αυτό δεν σημαίνει ότι είναι ίσα.

Αξία του συμβολαίου:

Η παραβίαση του συμβολαίου μπορεί να οδηγήσει σε λανθασμένη λειτουργία των συλλογών. Για παράδειγμα, αν δύο ίσα αντικείμενα επιστρέφουν διαφορετικούς κωδικούς κατακερματισμού, το HashMap μπορεί να τα τοποθετήσει σε διαφορετικά "καλάθια" και να μην μπορέσει να βρει ένα αντικείμενο μέσω του άλλου, ακόμα και αν είναι λογικά ίσα.

Παράδειγμα σωστής υλοποίησης:

// Παράδειγμα κλάσης με σωστά `equals` και `hashCode`
public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        int result = 17; // Μαγικό νούμερο, συνήθως πρώτο
        result = 31 * result + x;
        result = 31 * result + y;
        return result;
    }

    // Getters (προαιρετικά για το παράδειγμα `equals`/`hashCode`)
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

Χρήση:

  • Πάντα υπερκαλύψτε και τα δύο (equals και hashCode) μαζί.
  • Κατά τον υπολογισμό του hashCode, χρησιμοποιήστε τα ίδια πεδία που χρησιμοποιούνται στο equals.
  • Προσπαθήστε να δημιουργήσετε "καλά" functions hash που διανέμουν ομοιόμορφα τους κωδικούς hash, ώστε να ελαχιστοποιήσετε τις συγκρούσεις (όταν διαφορετικά αντικείμενα έχουν τον ίδιο κωδικό hash) και να βελτιώσετε την απόδοση των κατακερματισμένων συλλογών.
  • Για primitives, χρησιμοποιήστε τις τιμές τους. Για αντικείμενα, καλέστε το hashCode(). Για πίνακες, χρησιμοποιήστε τις μεθόδους από το Arrays.

Στο Java 7+, για ευκολία και σωστή λειτουργία, συνιστάται η χρήση των Objects.equals(Object a, Object b) και Objects.hash(Object... values).

Παράδειγμα με Objects:

// Παράδειγμα κλάσης με `equals` και `hashCode` χρησιμοποιώντας `Objects`
import java.util.Objects;

public final class PointWithObjects {
    private final int x;
    private final int y;

    public PointWithObjects(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PointWithObjects that = (PointWithObjects) o;
        return x == that.x && y == that.y; // Η λογική σύγκρισης παραμένει
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y); // Βολική μέθοδος για υπολογισμό hash
    }
}