здрасти Днес ще говорим за сравняване на обекти.
Хм... Но не сме ли говорor вече повече от веднъж на тази тема? :/ Знаем How
Кога да използвам
Да предположим например, че трябва да сортираме по този начин 15% от времето. Това очевидно не е достатъчно, за да настроим

==
работи операторът, 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то искаме! :) 
Comparable
? Методът за сравнение, внедрен в Comparable
се нарича естествено подреждане. Това е така, защото в compareTo()
метода вие дефинирате най-често срещания or естествен начин за сравняване на обекти от този клас. Java вече има естествено подреждане. Например Java знае, че низовете най-често се сортират по азбучен ред, а числата - по нарастваща числова стойност. Следователно, ако извикате sort()
метода на списък с числа or низове, те ще бъдат сортирани. Ако нашата програма обикновено сравнява и сортира автомобor по година на производство, тогава трябва да дефинираме естественото сортиране за автомобor с помощта на Comparable<Car>
интерфейса иcompareTo()
метод. Но Howво, ако това не ни е достатъчно? Нека си представим, че нашата програма не е толкова проста. В повечето случаи естественото сортиране на автомобor (което сме заложor да се извършва по година на производство) ни устройва. Но понякога нашите клиенти са любители на бързото шофиране. Ако подготвяме автомобилен каталог, който да разгледат, колите трябва да бъдат сортирани по максимална скорост. 
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то знаете, теорията без практика е нищо. Сега е време да затвърдите знанията си и да изпълните някои задачи!
GO TO FULL VERSION