CodeGym /Java блог /Случаен /Клас Comparator на Java
John Squirrels
Ниво
San Francisco

Клас Comparator на Java

Публикувано в групата
здрасти Днес ще говорим за сравняване на обекти. Клас за сравнение на Java - 1 Хм... Но не сме ли говорor вече повече от веднъж на тази тема? :/ Знаем How ==работи операторът, Howто и методите equals()и hashCode(). Сравнението е малко по-различно. Преди това най-вероятно имахме предвид „проверка на обекти за equalsство“. Но причините за сравняване на обекти един с друг могат да бъдат напълно различни! Най-очевидният от тях е сортирането. Мисля, че ако ви бъде казано да сортирате ArrayList<>числа or низове, ще можете да се справите с това без ниHowви проблеми:

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);
   }
}
Конзолен изход:

[Dasha, Masha, Sasha]
Ако сте запомнor Collectionsкласа и неговия sort()метод, браво! Мисля, че няма да имате проблеми и с числата. Ето една по-предизвикателна задача за вас:

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);
   }
}
Задачата всъщност е проста. Имаме Carклас и 3 автомобилни обекта. Бихте ли любезно сортирали колите в списъка? Вероятно ще попитате: "Как трябва да бъдат сортирани?" По име? По година на производство? С максимална скорост? Страхотен въпрос. В момента не знаем How да сортираме обектите Car. И, съвсем естествено, Java също не знае това! Когато се опитаме да предадем списък с Carобекти към Collections.sort()метода, получаваме грешка:

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);
   }
}
И наистина, How езикът ще знае How да сортира обекти от класове, които сте написали? Това зависи от това Howво трябва да прави вашата програма. Трябва по няHowъв начин да научим Java да сравнява тези обекти. И да ги сравняваме точно Howто искаме. Java има специален механизъм за това: интерфейсът Comparable. За да сравним и сортираме по няHowъв начин нашите Carобекти, класът трябва да имплементира този интерфейс, който се състои от един метод: 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;
   }

   // ...getters, setters, toString()

}
Моля обърнете вниманиече сме посочor Comparable<Car>интерфейса, а не само Comparable. Това е параметризиран интерфейс, тоест трябва да посочим конкретния асоцииран клас. По принцип можете да премахнете <Car>от интерфейса, но тогава сравнението ще се основава на Objectобекти по подразбиране. Вместо метода compareTo(Car o), нашият клас ще има:

@Override
   public int compareTo(Object o) {
       return 0;
   }
Разбира се, за нас е много по-лесно да работим с Car. Вътре в compareTo()метода ние прилагаме нашата логика за сравняване на автомобor. Да предположим, че трябва да ги сортираме по година на производство. Вероятно сте забелязали, че compareTo()методът връща int, а не boolean. Не позволявайте това да ви изненада. Когато сравняваме два обекта, има 3 възможности:
  • а < b
  • a > b
  • a == b.
booleanима само 2 стойности: true и false, което не работи добре за сравняване на обекти. С intвсичко е много по-просто. Ако върнатата стойност е > 0, тогава a > b. Ако резултатът от compareToе < 0, тогава a < b. И ако резултатът е == 0, тогава два обекта са равни: a == b. Лесно е да научим нашия клас да сортира коли по година на производство:

@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Но Howво става тук? Взимаме един обект Автомобил ( this), получаваме годината на производство на този автомобил и изваждаме от него годината на производство на друг автомобил (тази, с която обектът се сравнява). Ако първата година на производство на автомобила е по-голяма, методът ще върне int > 0. Това означава, че this car >колата o. Обратно, ако годината на производство на втората кола ( о) е по-голяма, тогава методът ще върне отрицателно число, което означава, че o > this. Накрая, ако са равни, тогава методът ще върне 0. Този прост механизъм вече ни е достатъчен, за да сортираме колекции от Carобекти! Не е нужно да правите нищо друго. Виж това:

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);
   }
}
Конзолен изход:

[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}]
Колите са сортирани Howто искаме! :) Клас Comparator на Java - 2Кога да използвам Comparable? Методът за сравнение, внедрен в Comparableсе нарича естествено подреждане. Това е така, защото в compareTo()метода вие дефинирате най-често срещания or естествен начин за сравняване на обекти от този клас. Java вече има естествено подреждане. Например Java знае, че низовете най-често се сортират по азбучен ред, а числата - по нарастваща числова стойност. Следователно, ако извикате sort()метода на списък с числа or низове, те ще бъдат сортирани. Ако нашата програма обикновено сравнява и сортира автомобor по година на производство, тогава трябва да дефинираме естественото сортиране за автомобor с помощта на Comparable<Car>интерфейса иcompareTo()метод. Но Howво, ако това не ни е достатъчно? Нека си представим, че нашата програма не е толкова проста. В повечето случаи естественото сортиране на автомобor (което сме заложor да се извършва по година на производство) ни устройва. Но понякога нашите клиенти са любители на бързото шофиране. Ако подготвяме автомобилен каталог, който да разгледат, колите трябва да бъдат сортирани по максимална скорост. Клас Comparator на Java - 3Да предположим например, че трябва да сортираме по този начин 15% от времето. Това очевидно не е достатъчно, за да настроим Carестественото сортиране на класа да бъде по скорост instead of по година на производство. Но не можем да пренебрегнем 15% от нашите клиенти. И така, Howво правим? Тук на помощ ни идва друг интерфейс: Comparator. Точно като Comparable, това е параметризиран интерфейс. Каква е разликата? Comparableправи нашите обекти "сравними" и определя техния най-естествен ред на сортиране, т.е. реда на сортиране, който ще се използва в повечето случаи. Comparatorе отделен интерфейс за "сравняване". Ако трябва да приложим няHowъв вид специален ред на сортиране, не е нужно да влизаме в Carкласа и да променяме логиката на compareTo(). Вместо това можем да създадем отделен клас, който прилага Comparator и да го научим How да извършва сортирането, от което се нуждаем!

import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {
  
   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Както можете да видите, нашата Comparatorе доста проста. Трябва да внедрим само един интерфейсен метод: compare(). Той приема два Carобекта като входни данни и сравнява техните максимални скорости по обичайния начин (чрез изваждане). Като compareTo(), връща an intи принципът на сравнение е същият. Как да използваме това? Всичко е просто:

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);
   }
}
Конзолен изход:

[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}]
Ние просто създаваме обект за сравнение и го предаваме на Collections.sort()метода заедно със списъка за сортиране. Когато sort()методът получи компаратор, той не използва естественото сортиране, дефинирано в метода Carна класа compareTo(). Вместо това той прилага алгоритъма за сортиране, дефиниран от предадения му компаратор. Какви са предимствата от това? Първо, съвместимост със съществуващия code. Създадохме нов, специален метод за сортиране, като същевременно запазихме съществуващия, който ще се използва през повечето време. CarИзобщо не пипахме класа. Беше Comparableи така си остава:

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()

}
Второ, гъвкавост. Можем да добавим колкото желаем алгоритми за сортиране. Например, можем да сортираме колите по цвят, скорост, тегло or по това колко пъти дадена кола е била използвана във филмите за Батман. Всичко, което трябва да направим, е да създадем допълнителен Comparator. Това е! Днес сте проучor два много важни механизма, които често ще използвате в реални проекти по време на работа. Но, Howто знаете, теорията без практика е нищо. Сега е време да затвърдите знанията си и да изпълните някои задачи!
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION