Salut! Aujourd'hui, nous allons parler de la comparaison d'objets.
Hmm... Mais n'avons-nous pas déjà parlé de ce sujet plus d'une fois ? :/ Nous connaissons le
Quand dois-je utiliser
Par exemple, supposons que nous devions trier ainsi 15 % du temps. Cela ne nous suffit évidemment pas pour définir le

==
fonctionnement de l'opérateur, ainsi que les méthodes equals()
et hashCode()
. La comparaison est un peu différente. Auparavant, nous voulions probablement dire "vérifier l'égalité des objets".
Mais les raisons de comparer des objets entre eux peuvent être complètement différentes ! Le plus évident d'entre eux est le tri. Je pense que si on vous disait de trier ArrayList<>
des nombres ou des chaînes, vous seriez capable de gérer cela sans aucun problème:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
String name1 = "Masha";
String name2 = "Sasha";
String name3 = "Dasha";
List<String> names = new ArrayList<>();
names.add(name1);
names.add(name2);
names.add(name3);
Collections.sort(names);
System.out.println(names);
}
}
Sortie console:
[Dasha, Masha, Sasha]
Si vous vous souvenez de la Collections
classe et de sa sort()
méthode, bravo ! Je pense que vous n'aurez également aucun problème avec les chiffres. Voici une tâche plus difficile pour vous :
public class Car {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
// ...getters, setters, toString()
}
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
}
}
La tâche est en fait simple. Nous avons une Car
classe et 3 objets Car. Pourriez-vous trier les voitures dans la liste ? Vous vous demanderez probablement : "Comment doivent-ils être triés ?" De nom? Par année de fabrication ? Par vitesse maximale?
Excellente question. Pour le moment, nous ne savons pas comment trier les Car
objets. Et, tout naturellement, Java ne le sait pas non plus ! Lorsque nous essayons de passer une liste d' Car
objets à la Collections.sort()
méthode, nous obtenons une erreur :
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(20012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// Compilation error!
Collections.sort(cars);
}
}
Et en effet, comment le langage saurait-il trier les objets des classes que vous avez écrites ? Cela dépend de ce que votre programme doit faire. Il faut en quelque sorte apprendre à Java à comparer ces objets. Et de les comparer comme on veut.
Java a un mécanisme spécial pour cela : l' Comparable
interface. Afin de comparer et de trier d'une manière ou d'une autre nos Car
objets, la classe doit implémenter cette interface, qui consiste en une seule méthodecompareTo()
:
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return 0;
}
// ...getters, setters, toString()
}
Veuillez noterque nous avons spécifié l' Comparable<Car>
interface, pas seulement Comparable
. Il s'agit d'une interface paramétrée, c'est-à-dire que nous devons spécifier la classe spécifique associée. En principe, vous pouvez supprimer <Car>
de l'interface, mais la comparaison sera alors basée sur Object
des objets par défaut. Au lieu de la compareTo(Car o)
méthode, notre classe aura :
@Override
public int compareTo(Object o) {
return 0;
}
Bien sûr, il est beaucoup plus facile pour nous de travailler avec Car
. À l'intérieur de la compareTo()
méthode, nous implémentons notre logique pour comparer les voitures. Supposons que nous ayons besoin de les trier par année de fabrication.
Vous avez probablement remarqué que la compareTo()
méthode renvoie un int
, pas un boolean
. Ne laissez pas cela vous surprendre. Quand on compare deux objets, il y a 3 possibilités :
а < b
a > b
a == b
.
boolean
n'a que 2 valeurs : vrai et faux, ce qui ne fonctionne pas bien pour comparer des objets. Avec int
, tout est beaucoup plus simple.
Si la valeur de retour est > 0
, alors a > b
. Si le résultat de compareTo
est < 0
, alors a < b
. Et, si le résultat est == 0
, alors deux objets sont égaux : a == b
. Enseigner à notre classe à trier les voitures par année de fabrication est facile :
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
Mais que se passe-t-il ici ? Nous prenons un objet Voiture ( this
), obtenons l'année de fabrication de cette voiture et en soustrayons l'année de fabrication d'une autre voiture (celle avec laquelle l'objet est comparé). Si l'année de fabrication de la première voiture est supérieure, la méthode renverra un int > 0
.
Cela signifie que la this car >
voiture o
. Inversement, si l'année de fabrication de la deuxième voiture ( о
) est supérieure, la méthode renverra un nombre négatif, ce qui signifie que o > this
. Enfin, s'ils sont égaux, la méthode retournera 0
. Ce mécanisme simple nous suffit déjà pour trier des collections d' Car
objets ! Vous n'avez rien d'autre à faire. Vérifiez-le:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
// There was previously an error here
Collections.sort(cars);
System.out.println(cars);
}
}
Sortie console:
[Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350},
Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290}]
Les voitures sont triées comme on veut ! :) 
Comparable
? La méthode de comparaison implémentée dans Comparable
s'appelle l'ordre naturel. En effet, dans la compareTo()
méthode, vous définissez la manière la plus courante, ou la plus naturelle, de comparer les objets de cette classe.
Java a déjà un ordre naturel. Par exemple, Java sait que les chaînes sont le plus souvent triées par ordre alphabétique et les nombres par valeur numérique croissante. Par conséquent, si vous appelez la sort()
méthode sur une liste de nombres ou de chaînes, ils seront triés.
Si notre programme compare et trie généralement les voitures par année de fabrication, nous devons définir le tri naturel des voitures à l'aide de l' Comparable<Car>
interface et de lacompareTo()
méthode. Et si cela ne nous suffisait pas?
Imaginons que notre programme ne soit pas si simple. Dans la plupart des cas, le tri naturel des voitures (que nous avons paramétré pour être effectué par année de fabrication) nous convient. Mais parfois nos clients sont des aficionados de la conduite rapide. Si nous préparons un catalogue de voitures à consulter, les voitures doivent être triées par vitesse maximale. 
Car
tri naturel de la classe par vitesse et non par année de fabrication. Mais nous ne pouvons pas ignorer 15 % de nos clients. Alors que faisons-nous?
Une autre interface nous vient ici en aide : Comparator
. Tout comme Comparable
, c'est une interface paramétrée. Quelle est la différence? Comparable
rend nos objets « comparables » et définit leur ordre de tri le plus naturel, c'est-à-dire l'ordre de tri qui sera utilisé dans la plupart des cas. Comparator
est une interface de "comparaison" distincte.
Si nous devons implémenter une sorte d'ordre de tri spécial, nous n'avons pas besoin d'aller dans la Car
classe et de changer la logique de compareTo()
. Au lieu de cela, nous pouvons créer une classe distincte qui implémente Comparator et lui apprendre à effectuer le tri dont nous avons besoin !
import java.util.Comparator;
public class MaxSpeedCarComparator implements Comparator<Car> {
@Override
public int compare(Car o1, Car o2) {
return o1.getMaxSpeed() - o2.getMaxSpeed();
}
}
Comme vous pouvez le voir, notre Comparator
est assez simple. Nous n'avons besoin d'implémenter qu'une seule méthode d'interface : compare()
. Il prend deux Car
objets en entrée et compare leurs vitesses maximales de la manière habituelle (par soustraction).
Comme compareTo()
, il renvoie le an int
, et le principe de comparaison est le même. Comment utilisons-nous cela? Tout est simple :
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
Car ferrari = new Car(1990, "Ferrari 360 Spider", 310);
Car lambo = new Car(2012, "Lamborghini Gallardo", 290);
Car bugatti = new Car(2010, "Bugatti Veyron", 350);
cars.add(ferrari);
cars.add(bugatti);
cars.add(lambo);
Comparator speedComparator = new MaxSpeedCarComparator();
Collections.sort(cars, speedComparator);
System.out.println(cars);
}
}
Sortie console:
[Car{manufactureYear=2012, model='Lamborghini Gallardo', maxSpeed=290},
Car{manufactureYear=1990, model='Ferrari 360 Spider', maxSpeed=310},
Car{manufactureYear=2010, model='Bugatti Veyron', maxSpeed=350}]
Nous créons simplement un objet comparateur et le transmettons à la Collections.sort()
méthode avec la liste à trier. Lorsque la sort()
méthode reçoit un comparateur, elle n'utilise pas le tri naturel défini dans la méthode Car
de la classe compareTo()
. Au lieu de cela, il applique l'algorithme de tri défini par le comparateur qui lui est transmis. Quels sont les avantages de faire cela?
Tout d'abord, la compatibilité avec le code existant. Nous avons créé une nouvelle méthode de tri spéciale, tout en conservant celle existante qui sera utilisée la plupart du temps. Nous n'avons pas Car
du tout touché à la classe. C'était un Comparable
, et il reste donc :
public class Car implements Comparable<Car> {
private int manufactureYear;
private String model;
private int maxSpeed;
public Car(int manufactureYear, String model, int maxSpeed) {
this.manufactureYear = manufactureYear;
this.model = model;
this.maxSpeed = maxSpeed;
}
@Override
public int compareTo(Car o) {
return this.getManufactureYear() - o.getManufactureYear();
}
// ...getters, setters, toString()
}
Deuxièmement, la flexibilité. Nous pouvons ajouter autant d'algorithmes de tri que nous le souhaitons. Par exemple, nous pouvons trier les voitures par couleur, vitesse, poids ou par combien de fois une voiture a été utilisée dans les films Batman. Tout ce que nous avons à faire est de créer un fichier Comparator
.
C'est ça! Aujourd'hui, vous avez étudié deux mécanismes très importants que vous utiliserez souvent dans de vrais projets au travail. Mais, comme vous le savez, la théorie sans la pratique n'est rien. Il est maintenant temps de consolider vos connaissances et d'effectuer certaines tâches !
GO TO FULL VERSION