1. Comparer des objets en Java

En Java, les objets peuvent être comparés à la fois par référence et par valeur.

Comparer des références

Si deux variables pointent vers le même objet en mémoire, alors les références stockées dans ces variables sont égales. Si vous comparez ces variables à l'aide de l'opérateur d'égalité ( ==), vous obtenez vrai et ce résultat est logique. Tout est simple ici.

Code Sortie console
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Comparer par valeur

Mais vous pouvez souvent rencontrer des situations où deux variables font référence à deux objets distincts qui sont identiques. Par exemple, deux objets chaînes différents qui contiennent le même texte.

Pour déterminer si différents objets sont identiques, utilisez la equals()méthode. Par exemple:

Code Sortie console
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

La equalsméthode n'est pas limitée à la Stringclasse. Chaque classe en a.

Même les cours que vous écrivez vous-même, et voici pourquoi.



2. Objectclasse

Toutes les classes en Java héritent de la Objectclasse. Les créateurs de Java ont proposé cette approche.

Et si une classe hérite de la Objectclasse, alors elle gagne toutes les méthodes de la Objectclasse. Et c'est une conséquence majeure de l'héritage.

En d'autres termes, chaque classe possède les méthodes de la Objectclasse, même si leur code ne les mentionne pas.

Ces méthodes héritées incluent des méthodes liées à la comparaison d'objets. Ce sont les méthodes equals()et hashCode().

Code En réalité, voici ce que nous aurons :
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

Dans l'exemple ci-dessus, nous avons créé une classe simple Personavec des paramètres de nom et d'âge, mais pas une seule méthode. Mais comme toutes les classes héritent de la Objectclasse, la Personclasse a automatiquement deux méthodes :

Méthode Description
boolean equals(Object obj)
Compare l'objet courant et l'objet passé
int hashCode()
Renvoie le hashcode de l'objet courant

Il s'avère qu'absolument chaque objet a la equalsméthode, et des objets de différents types peuvent être comparés les uns aux autres. Un tel code se compilera et fonctionnera parfaitement.

Code Sortie console
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()méthode

La equals()méthode, héritée de la Objectclasse, implémente l'algorithme le plus simple pour comparer l'objet courant avec les objets passés : elle compare simplement les références aux objets.

Vous obtenez le même résultat si vous comparez simplement Persondes variables au lieu d'appeler la equals()méthode. Exemple:

Code Sortie console
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

Lorsque la equalsméthode est appelée sur a, elle compare simplement la référence stockée dans la avariable avec la référence stockée dans la bvariable.

Cependant, la comparaison fonctionne différemment pour la Stringclasse. Pourquoi?

Parce que les gens qui ont créé la Stringclasse ont écrit leur propre implémentation de la equals()méthode.

Mise en œuvre de la equals()méthode

Écrivons maintenant notre propre implémentation de la méthode equals dans la Personclasse. Nous allons considérer 4 cas principaux.

Important:
Quelle que soit la classe qui remplace la equalsméthode, elle prend toujours un Objectobjet comme argument

Scénario 1 : le même objet sur lequel la equalsméthode est appelée est également passé à la equalsméthode. Si les références de l'objet courant et de l'objet passé sont égales, la méthode doit retourner true. Un objet est égal à lui-même.

Dans le code, cela ressemblera à ceci :

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Comparer les références

Scénario 2 : nullest passé à la equalsméthode — nous n'avons rien à comparer. L'objet sur lequel la equalsméthode est appelée n'est certainement pas nul, nous devons donc revenir falsedans ce cas.

Dans le code, cela ressemblera à ceci :

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Comparer les références


L'objet passé est-il null?

Scénario 3 : une référence à un objet qui n'est pas a Personest passée à la equalsméthode. L' Personobjet est-il égal au non- Personobjet ? C'est une question pour le développeur de la Personclasse de décider comme il le souhaite.

Mais généralement, les objets doivent appartenir à la même classe pour être considérés comme égaux. Par conséquent, si quelque chose d'autre qu'un objet de la Personclasse est passé à notre méthode equals, alors nous retournerons toujours false. Comment vérifier le type d'un objet ? C'est vrai — en utilisant l' instanceofopérateur.

Voici à quoi ressemble notre nouveau code :

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Comparer les références


L'objet passé est-il null?


Si l'objet passé n'est pas unPerson

4. Comparer deux Personobjets

Avec quoi avons-nous fini? Si nous avons atteint la fin de la méthode, alors nous avons une Personréférence d'objet qui n'est pas null. Nous le convertissons donc en a Personet comparons les données internes pertinentes des deux objets. Et c'est notre quatrième scénario .

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Comparer les références


L'objet passé est-il null?


Si l'objet passé n'est pas un Person


Typecasting

Et comment comparer deux Personobjets ? Ils sont égaux s'ils ont le même nom ( name) et le même âge ( age). Le code final ressemblera à ceci :

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Comparer les références


L'objet passé est-il null?


Si l'objet passé n'est pas un Person


Typecasting

Mais ce n'est pas tout.

Tout d'abord, le champ de nom est un String, vous devez donc comparer le champ de nom en appelant la equalsméthode.

this.name.equals(person.name)

Deuxièmement, le namechamp peut être null: dans ce cas, vous ne pouvez pas equalsl'appeler. Vous avez besoin d'un chèque supplémentaire pour null:

this.name != null && this.name.equals(person.name)

Cela dit, si le champ de nom se trouve nulldans les deux Personobjets, les noms sont toujours égaux.

Le code du quatrième scénario pourrait ressembler à ceci :

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


Si les âges ne sont pas égaux,
immédiatement return false

Si this.nameest égal à null, il n'y a pas lieu de comparer avec la equalsméthode. Ici, soit le deuxième namechamp est égal à null, soit il ne l'est pas.

Comparez les deux champs de nom à l'aide de la equalsméthode.


5. hashCode()méthode

En plus de la equalsméthode qui a pour but d'effectuer une comparaison détaillée de tous les champs des deux objets, il existe une autre méthode qui peut être utilisée pour une comparaison imprécise mais très rapide : hashCode().

Imaginez que vous triez par ordre alphabétique une liste de milliers de mots et que vous devez comparer à plusieurs reprises des paires de mots. Et les mots sont longs, composés de beaucoup de lettres. D'une manière générale, une telle comparaison prendrait beaucoup de temps.

Mais cela peut être accéléré. Supposons que nous ayons des mots qui commencent par des lettres différentes - il est immédiatement clair qu'ils sont différents. Mais s'ils commencent par les mêmes lettres, nous ne pouvons pas encore dire quel sera le résultat : les mots peuvent s'avérer égaux ou différents.

La hashCode()méthode fonctionne selon un principe similaire. Si vous l'appelez sur un objet, il renvoie un certain nombre - analogue à la première lettre d'un mot. Ce nombre a les propriétés suivantes :

  • Des objets identiques ont toujours le même hashcode
  • Différents objets peuvent avoir le même hashcode, ou leurs hashcodes peuvent être différents
  • Si les objets ont des hashcodes différents, alors les objets sont définitivement différents

Pour rendre cela encore plus clair, recadrons ces propriétés en termes de mots :

  • Des mots identiques ont toujours les mêmes premières lettres.
  • Différents mots peuvent avoir les mêmes premières lettres, ou leurs premières lettres peuvent être différentes
  • Si les mots ont des premières lettres différentes, alors les mots sont définitivement différents

La dernière propriété est utilisée pour accélérer la comparaison des objets :

Tout d'abord, les hashcodes des deux objets sont calculés. Si ces hashcodes sont différents, alors les objets sont définitivement différents et il n'est pas nécessaire de les comparer davantage.

Mais si les hashcodes sont les mêmes, nous devons encore comparer les objets en utilisant la méthode equals.



6. Contrats en code

Le comportement décrit ci-dessus doit être implémenté par toutes les classes en Java. Lors de la compilation, il n'existe aucun moyen de vérifier si les objets sont correctement comparés.

Les programmeurs Java ont un accord universel sur le fait que s'ils écrivent leur propre implémentation de la méthode equals () et remplacent ainsi l'implémentation standard (dans la Objectclasse), ils doivent également écrire leur propre implémentation de la hashCode()méthode de manière à ce que les règles susmentionnées soient satisfait.

Cet arrangement s'appelle un contrat .

Si vous implémentez uniquement la equals()ou uniquement la hashCode()méthode dans votre classe, vous êtes en violation flagrante du contrat (vous avez rompu l'accord). Ne fais pas ça.

Si d'autres programmeurs utilisent votre code, il se peut qu'il ne fonctionne pas correctement. De plus, vous utiliserez un code qui repose sur le respect des contrats ci-dessus.

Important!

Lors de la recherche d'un élément, toutes les collections Java comparent d'abord les codes de hachage des objets, puis effectuent ensuite une comparaison à l'aide de la equalsméthode.

Cela signifie que si vous donnez une equalsméthode à votre propre classe mais que vous n'écrivez pas votre propre hashCode()méthode ou que vous l'implémentez de manière incorrecte, les collections peuvent ne pas fonctionner correctement avec vos objets.

Par exemple, vous pouvez ajouter un objet à une liste, puis le rechercher à l'aide de la contains()méthode, mais la collection risque de ne pas trouver votre objet.