JavaRush /Blog Java /Random-VI /Bộ so sánh trong Java

Bộ so sánh trong Java

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ nói về việc so sánh các đối tượng. Hmm... Nhưng có vẻ như chúng ta đã nói về vấn đề này nhiều lần rồi nhỉ? :/ Chúng ta biết toán tử “ ==” hoạt động như thế nào cũng như các phương thức equals()hashCode(). So sánh không thực sự là về điều đó. Trước đây, chúng tôi muốn nói giống như “kiểm tra sự bình đẳng của các đối tượng”. Trình so sánh trong Java - 1Nhưng việc so sánh các đồ vật với nhau có thể có những mục tiêu hoàn toàn khác nhau! Điều rõ ràng nhất là sắp xếp. Tôi nghĩ nếu bạn được yêu cầu sắp xếp danh sách ArrayList<>các số hoặc chuỗi, bạn có thể xử lý nó mà không gặp vấn đề gì:
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);
   }
}
Đầu ra của bảng điều khiển:

[Даша, Маша, Саша]
Thật tuyệt nếu bạn nhớ được lớp Collectionsvà phương thức của nó sort(). Tôi cũng không nghĩ sẽ có vấn đề gì với các con số. Đây là một nhiệm vụ khó khăn hơn cho bạn:
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);
   }
}
Rất đơn giản: một lớp Carvà 3 đối tượng của nó. Hãy tử tế để sắp xếp những chiếc xe trong danh sách! Có thể bạn sẽ hỏi: “Chúng nên được sắp xếp như thế nào?” Theo tên, theo năm sản xuất, theo tốc độ tối đa? Câu hỏi tuyệt vời. Hiện tại chúng tôi không biết cách sắp xếp các đối tượng của lớp Car. Và tất nhiên, Java cũng không biết điều này! Khi chúng ta cố gắng truyền Collections.sort()danh sách các đối tượng cho một phương thức Car, chúng ta sẽ gặp lỗi:
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);
   }
}
Và thực sự, làm thế nào ngôn ngữ biết chính xác cách sắp xếp các đối tượng bạn viết? Nó phụ thuộc vào mục tiêu của chương trình của bạn. Bằng cách nào đó chúng ta phải dạy Java cách so sánh các đối tượng này. Và so sánh theo cách chúng ta cần nó. Với mục đích này, Java có một công cụ đặc biệt - giao diện Comparable. Trong tiếng Anh, từ này được dịch là “có thể so sánh được”. Để các đối tượng của chúng ta Carcó thể so sánh với nhau và được sắp xếp theo cách nào đó, lớp phải triển khai giao diện này và triển khai phương thức duy nhất của nó: 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()

}
Chú ý:chúng tôi đã chỉ định giao diện Comparable<Car>chứ không chỉ Comparable. Nó là một giao diện được định kiểu, nghĩa là nó yêu cầu đặc tả lớp cụ thể mà nó liên kết. Về nguyên tắc, <Car>bạn có thể xóa nó khỏi giao diện nhưng sau đó nó sẽ so sánh các đối tượng theo mặc định Object. Thay vì một phương thức compareTo(Car o)trong lớp, chúng ta sẽ có:
@Override
   public int compareTo(Object o) {
       return 0;
   }
Tất nhiên, chúng tôi làm việc với Car. Bên trong phương thức này compareTo(), chúng tôi triển khai logic để so sánh các máy. Giả sử chúng ta cần sắp xếp chúng theo năm sản xuất. Bạn có thể nhận thấy rằng phương thức này compareTo()trả về giá trị intchứ không phải boolean. Đừng để điều này làm bạn ngạc nhiên. Thực tế là việc so sánh hai đối tượng cho chúng ta 3 lựa chọn khả thi:
  • а < b
  • a > b
  • a == b.
booleanchỉ có 2 giá trị - đúng và sai, gây bất tiện cho việc so sánh các đối tượng. Mọi thứ intđơn giản hơn nhiều. Nếu giá trị trả về > 0là , thì a > b. Nếu kết quả compareTo < 0là , thì а < b. Vâng, nếu kết quả là == 0, thì hai đối tượng bằng nhau: a == b. Dạy lớp chúng ta cách phân loại ô tô theo năm sản xuất dễ như bóc vỏ quả lê:
@Override
public int compareTo(Car o) {
   return this.getManufactureYear() - o.getManufactureYear();
}
Những gì đang xảy ra ở đây? Chúng ta lấy một đối tượng ô tô ( this), năm sản xuất của chiếc ô tô này và trừ đi năm sản xuất của một chiếc ô tô khác ( chiếc mà chúng ta so sánh với đối tượng đó). Nếu năm sản xuất của chiếc ô tô đầu tiên lớn hơn, phương pháp sẽ trả về int > 0. Có nghĩa là một chiếc xe hơi this >là một chiếc xe hơi о. Ngược lại, nếu năm sản xuất của chiếc ô tô thứ hai ( о) lớn hơn thì phương thức sẽ trả về số âm và do đó о > this. Chà, nếu chúng bằng nhau, phương thức sẽ trả về 0. Một cơ chế đơn giản như vậy đã đủ để sắp xếp các bộ sưu tập đối tượng Car! Bạn không cần phải làm gì khác. Của bạn đây:
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);
   }
}
Đầu ra của bảng điều khiển:

[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}]
Những chiếc xe được sắp xếp như bình thường! :) Bộ so sánh trong Java - 2Nó nên được sử dụng trong trường hợp nào Comparable? Phương pháp so sánh được thực hiện Comparableđược gọi là “thứ tự tự nhiên”. Điều này là do trong phương pháp này compareTo()bạn mô tả phương pháp so sánh phổ biến nhất sẽ được sử dụng cho các đối tượng thuộc lớp này trong chương trình của bạn. Thứ tự tự nhiên đã có trong Java. Ví dụ: Java biết rằng các chuỗi thường được sắp xếp theo thứ tự bảng chữ cái và các số thường được sắp xếp theo giá trị tăng dần. Do đó, nếu bạn gọi phương thức trên danh sách số hoặc chuỗi sort(), chúng sẽ được sắp xếp. Nếu trong chương trình của chúng tôi, ô tô trong hầu hết các trường hợp sẽ được so sánh và sắp xếp theo năm sản xuất, thì cần xác định loại tự nhiên cho chúng bằng cách sử dụng giao diện Comparable<Car>và phương thức compareTo(). Nhưng nếu như thế vẫn chưa đủ với chúng ta thì sao? Hãy tưởng tượng rằng chương trình của chúng ta không đơn giản như vậy. Trong hầu hết các trường hợp, cách phân loại ô tô tự nhiên (chúng tôi đặt theo năm sản xuất) phù hợp với chúng tôi. Nhưng đôi khi trong số khách hàng của chúng tôi có những người thích lái xe nhanh. Nếu chúng ta chuẩn bị một danh mục ô tô để họ lựa chọn thì chúng cần phải được đặt hàng theo tốc độ tối đa. Trình so sánh trong Java - 3Ví dụ: chúng tôi cần phân loại như vậy trong 15% trường hợp. Điều này rõ ràng là không đủ để thiết lập phân loại tự nhiên Cartheo tốc độ thay vì năm sản xuất. Nhưng chúng ta không thể bỏ qua 15% khách hàng. Chúng ta làm gì? Ở đây có một giao diện khác hỗ trợ chúng ta - Comparator. Giống như , Comparablenó được gõ. Có gì khác biệt? Comparablelàm cho các đối tượng của chúng ta có thể so sánh được và tạo ra thứ tự sắp xếp tự nhiên nhất cho chúng sẽ được sử dụng trong hầu hết các trường hợp. Comparator- đây là một lớp “so sánh” riêng biệt (bản dịch hơi vụng về nhưng có thể hiểu được). Nếu cần triển khai một số cách sắp xếp cụ thể, chúng ta không cần phải vào lớp Carvà thay đổi logic compareTo(). Thay vào đó, chúng ta có thể tạo một lớp so sánh riêng trong chương trình của mình và dạy nó cách sắp xếp mà chúng ta cần!
import java.util.Comparator;

public class MaxSpeedCarComparator implements Comparator<Car> {

   @Override
   public int compare(Car o1, Car o2) {
       return o1.getMaxSpeed() - o2.getMaxSpeed();
   }
}
Như bạn có thể thấy, việc của chúng tôi Comparatorkhá đơn giản. Chỉ có một phương thức compare()- đây là phương thức giao diện Comparator, phải được triển khai. Nó lấy hai đối tượng làm đầu vào Carvà so sánh tốc độ tối đa của chúng theo cách thông thường (bằng phép trừ). Giống như compareTo(), nó trả về số int, nguyên tắc so sánh cũng như vậy. Làm thế nào chúng ta có thể sử dụng điều này? Rất đơn giản:
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);
   }
}
Đầu ra của bảng điều khiển:

[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}]
Chúng ta chỉ cần tạo một đối tượng so sánh và chuyển nó vào phương thức Collections.sort()cùng với danh sách cần sắp xếp. Sau khi nhận được bộ so sánh làm đầu vào, phương thức sort()sẽ không sử dụng cách sắp xếp tự nhiên được xác định trong phương thức compareTo()lớp Car. Thay vào đó, nó sẽ áp dụng thuật toán sắp xếp từ bộ so sánh được truyền vào nó. Điều này mang lại cho chúng ta những lợi thế gì? Thứ nhất, khả năng tương thích với mã viết. Chúng tôi đã tạo một phương pháp sắp xếp mới, cụ thể trong khi vẫn duy trì phương pháp hiện tại. Phương pháp này sẽ được sử dụng trong hầu hết các trường hợp. Chúng tôi không hề chạm vào lớp học Car. Anh Comparablevẫn như cũ:
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()

}
Thứ hai, tính linh hoạt. Chúng ta có thể thêm bao nhiêu loại tùy thích. Giả sử sắp xếp ô tô theo màu sắc, tốc độ, trọng lượng hoặc số lần chiếc ô tô được sử dụng trong phim Batman. Chỉ cần tạo thêm một cái là đủ Comparator. Đó là tất cả! Hôm nay bạn đã học được hai cơ chế rất quan trọng mà bạn sẽ thường sử dụng trong các dự án thực tế tại nơi làm việc. Nhưng như bạn đã biết, lý thuyết mà không thực hành thì chẳng là gì cả. Vì vậy, đã đến lúc củng cố kiến ​​thức của bạn và giải quyết một số vấn đề! :)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION