JavaRush/Java Blog/Random EN/Comparator in Java

Comparator in Java

Published in the Random EN group
members
Hello! Today we will talk about comparing objects. Hmm... But we seem to have already talked about this more than once? :/ We know how the “ ==” operator works, as well as the equals()and methods hashCode(). Comparison isn't really about that. Previously, we meant more like “testing objects for equality.” Comparator in Java - 1But comparing objects with each other may have completely different goals! The most obvious one is sorting. I think if you are told to sort a list of ArrayList<>numbers or strings, you can handle it without problems:
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 = "Даша";

       List<String> names = new ArrayList<>();
       names.add(name1);
       names.add(name2);
       names.add(name3);

       Collections.sort(names);
       System.out.println(names);
   }
}
Console output:

[Даша, Маша, Саша]
It’s great if you remembered the class Collectionsand its method sort(). I don’t think there will be any problems with numbers either. Here's a more difficult task for you:
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;
   }

   //...геттеры, сеттеры, 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);
   }
}
It’s very simple: a class Carand 3 of its objects. Be so kind as to sort the cars in the list! You will probably ask: “How should they be sorted?” By name, by year of manufacture, by maximum speed? Great question. We do not know at the moment how to sort objects of the class Car. And, quite naturally, Java doesn’t know this either! When we try to pass Collections.sort()a list of objects to a method Car, we will receive an error:
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);

       //ошибка компилятора!
       Collections.sort(cars);
   }
}
And really, how does the language know exactly how to sort the objects you write? It depends on the goals of your program. We have to somehow teach Java to compare these objects. And compare the way we need it. For this purpose, Java has a special tool - interface Comparable. In English this is translated as “comparable”. In order for our objects Carto be compared with each other and somehow sorted, the class must implement this interface and implement its only method: compareTo():
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;
   }

   //...геттеры, сеттеры, toString()

}
Pay attention:we specified the interface Comparable<Car>, not just Comparable. This is a typed interface, that is, it requires specifying the specific class with which it is associated. In principle, <Car>you can remove it from the interface, but then it compares objects by default Object. Instead of a method compareTo(Car o)in our class we will have:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Of course, it’s much easier for us to work with Car. Inside the method compareTo()we implement the logic for comparing machines. Let's say we need to sort them by year of manufacture. You probably noticed that the method compareTo()returns the value int, not boolean. Don't let this surprise you. The fact is that comparing two objects gives us 3 possible options:
  • а < b
  • a > b
  • a == b.
It booleanhas only 2 values ​​- true and false, which is inconvenient for comparing objects. Everything is intmuch simpler. If the return value > 0is , then a > b. If the result compareTo < 0is , then а < b. Well, if the result is == 0, then the two objects are equal: a == b. Teaching our class to sort cars by year of manufacture is as easy as shelling pears:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
What's going on here? We take one car object ( this), the year of manufacture of this car, and subtract from it the year of manufacture of another car (the one with which we compare the object). If the year of manufacture of the first car is greater, the method will return int > 0. Which means a car this >is a car о. If, on the contrary, the year of manufacture of the second car ( о) is greater, then the method will return a negative number, and therefore о > this. Well, if they are equal, the method will return 0. Such a simple mechanism is already enough to sort collections of objects Car! You don't need to do anything else. Here you are:
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);

       //тут раньше была ошибка
       Collections.sort(cars);
       System.out.println(cars);
   }
}
Console output:

[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}]
The cars are sorted as they should be! :) Comparator in Java - 2In what cases should it be used Comparable? The comparison method implemented Comparableis called “natural ordering”. This is because in the method compareTo()you describe the most common comparison method that will be used for objects of this class in your program. Natural Ordering is already present in Java. For example, Java knows that strings are most often sorted alphabetically, and numbers are most often sorted by ascending value. Therefore, if you call the method on a list of numbers or strings sort(), they will be sorted. If in our program cars will in most cases be compared and sorted by year of manufacture, then it is worth defining a natural sort for them using the interface Comparable<Car>and method compareTo(). But what if that's not enough for us? Let's imagine that our program is not so simple. In most cases, the natural sorting of cars (we set it by year of manufacture) suits us. But sometimes among our clients there are fans of fast driving. If we are preparing a catalog of cars for them to choose from, they need to be ordered by maximum speed. Comparator in Java - 3For example, we need such sorting in 15% of cases. This is clearly not enough to set natural sorting for Carby speed instead of year of manufacture. But we cannot ignore 15% of clients. What do we do? Here another interface comes to our aid - Comparator. Just like , Comparableit is typed. What's the difference? Comparablemakes our objects "comparable" and creates the most natural sort order for them that will be used in most cases. Comparator- this is a separate “comparator” class (the translation is a little clumsy, but understandable). If we need to implement some specific sorting, we don’t have to go into the class Carand change the logic compareTo(). Instead, we can create a separate comparator class in our program and teach it to do the sorting we need!
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
As you can see, ours Comparatoris quite simple. There is only one method compare()- this is an interface method Comparator, which must be implemented. It takes two objects as input Carand compares their maximum speed in the usual way (by subtraction). Like compareTo(), it returns the number int, the comparison principle is the same. How can we use this? Very 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);
   }
}
Console output:

[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}]
We simply create a comparator object and pass it to the method Collections.sort()along with the list to be sorted. Having received a comparator as input, the method sort()will not use the natural sorting defined in the compareTo()class method Car. Instead, it will apply the sorting algorithm from the comparator passed to it. What advantages does this give us? Firstly, compatibility with the written code. We created a new, specific sorting method, while maintaining the current one, which will be used in most cases. We didn't touch the class at all Car. He Comparableremained as he was:
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();
   }

   //...геттеры, сеттеры, toString()

}
Secondly, flexibility. We can add as many sorts as we want. Say, sort cars by color, speed, weight, or how many times the car was used in Batman movies. It is enough just to create an additional one Comparator. That's all! Today you learned two very important mechanisms that you will often use in real projects at work. But, as you know, theory without practice is nothing. Therefore, it’s time to consolidate your knowledge and solve several problems! :)
Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet