CodeGym /Cours /JAVA 25 SELF /Classe Objects : méthodes equals, hashCode, hash

Classe Objects : méthodes equals, hashCode, hash

JAVA 25 SELF
Niveau 29 , Leçon 1
Disponible

1. Découverte de la classe Objects

Passons au sujet ! Si vous en avez assez de vérifier manuellement les valeurs sur null et d’écrire le calcul du hachage pour chaque champ, la classe utilitaire java.util.Objects vous sauvera la mise. Son rôle — rendre le travail avec les objets plus simple, plus concis et plus sûr.

C’est vraiment un « couteau suisse » : la classe sait comparer des objets pour l’égalité en toute sécurité (sans risque de NullPointerException), calculer commodément des codes de hachage, comparer via un comparateur et vérifier les arguments par rapport à null.

Objects.equals : comparaison sûre tenant compte de null

Si vous écrivez simplement a.equals(b), et que a vaut null, vous obtiendrez un NullPointerException. Les vérifications manuelles sont lourdes. Objects.equals(a, b) fait tout pour vous :

  • Si les deux valent null — renvoie true.
  • Si un seul vaut null — renvoie false.
  • Si les deux ne sont pas null — appelle l’equals habituel.
import java.util.Objects;

String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false

String c = null;
System.out.println(Objects.equals(a, c)); // true

String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true

Pourquoi est-ce pratique ? Le code est plus court, plus clair et protégé des NPE accidentels.

2. Objects.hash et hashCode : calcul de hachage concis

En redéfinissant hashCode avec equals, il est facile de se tromper, surtout quand il y a beaucoup de champs. Le code manuel est souvent verbeux et fragile :

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + age;
    return result;
}

La méthode Objects.hash résout le problème — concis, sûr et gère null :

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    // ... constructeur, getters, etc.

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

Point important : Objects.hash utilise des varargs et crée un tableau — dans de rares zones très sollicitées, un hashCode manuel peut être plus rapide. Pour la plupart des applications, la différence est négligeable.

3. Objects.compare : déléguer la comparaison à un comparateur

Parfois, il faut comparer deux objets à l’aide d’un Comparator préparé à l’avance. Au lieu d’appeler directement comparator.compare(a, b), vous pouvez utiliser :

int result = Objects.compare(a, b, comparator);

Cette méthode :

  • Retourne 0 si les objets sont égaux.
  • Considère null comme « inférieur » à tout objet non-null.
  • Dans les autres cas, délègue la logique au comparateur passé.
import java.util.Comparator;
import java.util.Objects;

class Person {
    private String name;
    Person(String name) { 
        this.name = name; 
    }
    public String getName() { 
        return name; 
    }
}

public class Main {
    public static void main(String[] args) {
        Person a = new Person("Anna");
        Person b = new Person("Boris");
        Comparator<Person> byName = Comparator.comparing(Person::getName);

        System.out.println(Objects.compare(a, b, byName)); // <0, car "Anna" < "Boris"
        System.out.println(Objects.compare(a, null, byName)); // >0, car a != null
        System.out.println(Objects.compare(null, b, byName)); // <0, car null < b
        System.out.println(Objects.compare(null, null, byName)); // 0
    }
}

4. Objects.requireNonNull : assurance contre les erreurs « invisibles »

Si une méthode doit n’accepter que des valeurs non-null, vérifiez-le immédiatement. Objects.requireNonNull lèvera un NullPointerException avec votre message :

public void setName(String name) {
    this.name = Objects.requireNonNull(name, "Le nom ne peut pas être null");
}

5. Exemple : implémentation correcte de equals, hashCode et compareTo avec Objects

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = Objects.requireNonNull(name, "Le nom ne peut pas être null");
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // Comparaison par référence
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        // Comparaison sûre avec null
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age); // Concis et sûr
    }

    @Override
    public int compareTo(Person other) {
        // D’abord comparer par le nom, puis par l’âge
        int cmp = name.compareTo(other.name);
        if (cmp != 0) return cmp;
        return Integer.compare(age, other.age);
    }
}

Vous pouvez maintenant stocker des objets dans un HashSet, les utiliser comme clés dans une HashMap, tester l’égalité et trier des listes (par exemple via Collections.sort).

6. Applications concrètes : réduction du code et diminution des erreurs

Exemple : liste d’utilisateurs dans une application

Grâce à une paire equals/hashCode correcte, la recherche dans les collections fonctionne de manière prévisible :

import java.util.ArrayList;
import java.util.List;

List<Person> users = new ArrayList<>();
users.add(new Person("Anna", 25));
users.add(new Person("Boris", 30));

Person search = new Person("Anna", 25);
System.out.println(users.contains(search)); // true

Exemple : travail avec des champs pouvant être null

Si une classe possède des champs qui peuvent être null (par exemple, le deuxième prénom), utilisez Objects.equals et Objects.hash :

import java.util.Objects;

public class User {
    private String firstName;
    private String middleName; // Peut être null
    private String lastName;

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

    @Override
    public int hashCode() {
        return Objects.hash(firstName, middleName, lastName);
    }
}

7. Tableau : principales méthodes de la classe Objects

Méthode Finalité Exemple d’utilisation
Objects.equals(a, b)
Comparaison sûre de deux objets en tenant compte de null
Objects.equals(a, b)
Objects.hash(a, b, ...)
Calcul concis du code de hachage sur plusieurs champs
Objects.hash(name, age)
Objects.compare(a, b, comparator)
Comparaison via un comparateur, sûre pour null
Objects.compare(p1, p2, byNameComparator)
Objects.requireNonNull(obj[, msg])
Vérification de null, lève NullPointerException
Objects.requireNonNull(name, "Le nom ne peut pas être null")
Objects.isNull(obj) / Objects.nonNull(obj)
Test de null/non null (pratique avec le Stream API)
list.stream().filter(Objects::nonNull)

8. Erreurs types lors de l’utilisation des méthodes de la classe Objects

Erreur n° 1 : oubli d’utiliser Objects.equals pour les champs nullable. Si vous comparez les champs directement via equals, vous pouvez déclencher un NullPointerException. Utilisez Objects.equals(middleName, other.middleName).

Erreur n° 2 : tous les champs ne sont pas pris en compte dans hashCode. Les champs qui participent à equals doivent aussi participer à hashCode, sinon le comportement de HashSet/HashMap devient imprévisible.

Erreur n° 3 : hashCode manuel erroné. Respecter les coefficients et les vérifications sur null n’est pas trivial. Objects.hash le fait pour vous ; utilisez-le s’il n’y a pas d’exigences strictes de performance.

Erreur n° 4 : vous n’utilisez pas Objects.requireNonNull là où c’est le contrat de la classe. Si un champ ne doit pas accepter null, vérifiez-le dans le constructeur/le setter — l’erreur apparaîtra immédiatement, et non « dans les méandres » de la pile d’appels.

Erreur n° 5 : vous utilisez Objects.hash pour des tableaux. Pour les tableaux, utilisez Arrays.hashCode, et pour les tableaux imbriqués — Arrays.deepHashCode ; de même, pour comparer le contenu, utilisez Arrays.equals/Arrays.deepEquals.

1
Mission
JAVA 25 SELF, niveau 29, leçon 1
Bloqué
Création d'une empreinte numérique pour les livres du catalogue 📚
Création d'une empreinte numérique pour les livres du catalogue 📚
1
Mission
JAVA 25 SELF, niveau 29, leçon 1
Bloqué
Tri des participants de l'événement par nom 🧑‍🤝‍🧑
Tri des participants de l'événement par nom 🧑‍🤝‍🧑
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION