Tiếng Việt (Vietnamese)

Java - Cách sử dụng Comparator?

Xin chào các nhà pháp sư Java tương lai! ? Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới của Java Comparators. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình - tôi sẽ là người hướng dẫn bạn, và chúng ta sẽ cùng nhau từng bước. Cuối cùng của bài hướng dẫn này, bạn sẽ có khả năng sắp xếp các đối tượng như một chuyên gia!

Java - Comparators

Comparator là gì?

Trước khi chúng ta đi sâu vào, hãy tưởng tượng bạn đang sắp xếp kệ sách của mình. Bạn có thể muốn sắp xếp sách theo tựa đề, tác giả hoặc ngày xuất bản. Trong Java, một Comparator giống như một thư viện cá nhân biết chính xác cách sắp xếp bộ sưu tập của bạn dựa trên bất kỳ tiêu chí nào bạn chọn.

Về kỹ thuật, một Comparator là một giao diện trong Java cho phép chúng ta xác định thứ tự tùy chỉnh cho các đối tượng. Nó đặc biệt hữu ích khi chúng ta muốn sắp xếp các đối tượng không có thứ tự tự nhiên, hoặc khi chúng ta muốn sắp xếp chúng theo cách khác với thứ tự tự nhiên của chúng.

Giao diện Comparator

Hãy cùng nhìn kỹ hơn vào giao diện Comparator:

public interface Comparator<T> {
int compare(T o1, T o2);
}

Đừng để điều này làm bạn sợ hãi! Nó đơn giản hơn bạn nghĩ. <T> chỉ là cách nói rằng giao diện này có thể hoạt động với bất kỳ loại đối tượng nào. Phương thức compare là nơi xảy ra phép màu - nó giống như yêu cầu thư viện của bạn so sánh hai cuốn sách.

Cách phương thức compare hoạt động

Phương thức compare nhận hai đối tượng và trả về một số nguyên:

  • Nếu đối tượng đầu tiên được coi là "nhỏ hơn" đối tượng thứ hai, nó trả về một số âm.
  • Nếu chúng được coi là "bằng nhau", nó trả về không.
  • Nếu đối tượng đầu tiên được coi là "lớn hơn" đối tượng thứ hai, nó trả về một số dương.

Hãy tưởng tượng nó như một cân. Nếu đối tượng đầu tiên nhẹ hơn, cân sẽ nghiêng về phía số âm. Nếu chúng bằng nhau, cân sẽ cân bằng ở zero. Nếu đối tượng đầu tiên nặng hơn, cân sẽ nghiêng về phía số dương.

Tạo Comparator đầu tiên

Hãy tạo một Comparator đơn giản để sắp xếp các chuỗi theo độ dài. Chúng ta sẽ gọi nó là StringLengthComparator:

import java.util.Comparator;

public class StringLengthComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}

Đây là những gì đang xảy ra:

  1. Chúng ta nhập giao diện Comparator từ java.util.
  2. Chúng ta tạo một lớp triển khai Comparator<String>, có nghĩa là nó sẽ so sánh các đối tượng String.
  3. Chúng ta ghi đè phương thức compare để trừ độ dài của chuỗi thứ hai từ chuỗi đầu tiên.

Sử dụng Comparator của bạn

Bây giờ chúng ta đã có Comparator của mình, hãy sử dụng nó! Chúng ta sẽ tạo một danh sách các chuỗi và sắp xếp chúng:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ComparatorExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Pear");
fruits.add("Banana");
fruits.add("Kiwi");

System.out.println("Trước khi sắp xếp: " + fruits);

Collections.sort(fruits, new StringLengthComparator());

System.out.println("Sau khi sắp xếp: " + fruits);
}
}

Kết quả đầu ra:

Trước khi sắp xếp: [Apple, Pear, Banana, Kiwi]
Sau khi sắp xếp: [Pear, Kiwi, Apple, Banana]

Đây là những gì đang xảy ra:

  1. Chúng ta tạo một danh sách các tên trái cây.
  2. Chúng ta in danh sách ban đầu.
  3. Chúng ta sử dụng Collections.sort() với Comparator tùy chỉnh để sắp xếp danh sách.
  4. Chúng ta in danh sách đã sắp xếp.

Lưu ý cách các trái cây bây giờ được sắp xếp theo độ dài tên của chúng!

Lambda Expressions: Cách rút ngắn

Java 8 đã giới thiệu lambda expressions, giúp Comparator của chúng ta trở nên ngắn gọn hơn. Dưới đây là cùng một ví dụ sử dụng lambda:

Collections.sort(fruits, (s1, s2) -> s1.length() - s2.length());

Câu này làm exactly the same thing như lớp StringLengthComparator của chúng ta! Nó giống như nói với Java, "Hey, khi bạn so sánh hai chuỗi, chỉ cần trừ độ dài của chúng."

Sắp xếp các đối tượng tùy chỉnh

Bây giờ, hãy nâng cấp và sắp xếp một số đối tượng tùy chỉnh. Hãy tưởng tượng chúng ta có một lớp Person:

public class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

// Các phương thức getter và setter...

@Override
public String toString() {
return name + " (" + age + ")";
}
}

Chúng ta có thể tạo một Comparator để sắp xếp các đối tượng Person theo tuổi:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class PersonSortExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
people.add(new Person("Charlie", 22));

System.out.println("Trước khi sắp xếp: " + people);

Collections.sort(people, Comparator.comparingInt(Person::getAge));

System.out.println("Sau khi sắp xếp: " + people);
}
}

Kết quả đầu ra:

Trước khi sắp xếp: [Alice (25), Bob (30), Charlie (22)]
Sau khi sắp xếp: [Charlie (22), Alice (25), Bob (30)]

Ở đây, chúng ta sử dụng Comparator.comparingInt(), tạo một Comparator dựa trên giá trị nguyên - trong trường hợp này, tuổi. Person::getAge là một tham chiếu phương thức, cho biết Java sử dụng phương thức getAge() để lấy giá trị để so sánh.

Đảo ngược thứ tự

Vậy nếu chúng ta muốn sắp xếp theo thứ tự giảm dần thì sao? Dễ lắm! Chỉ cần sử dụng phương thức reversed():

Collections.sort(people, Comparator.comparingInt(Person::getAge).reversed());

Điều này sẽ sắp xếp các đối tượng Person từ lớn tuổi đến trẻ tuổi.

Chaining Comparators

Đôi khi, chúng ta có thể muốn sắp xếp theo nhiều tiêu chí. Ví dụ, hãy sắp xếp các đối tượng Person theo tuổi, và nếu tuổi bằng nhau, theo tên:

Comparator<Person> ageAndNameComparator = Comparator
.comparingInt(Person::getAge)
.thenComparing(Person::getName);

Collections.sort(people, ageAndNameComparator);

Điều này tạo một Comparator rằng trước tiên so sánh tuổi, và nếu chúng bằng nhau, so sánh tên.

Kết luận

Chúc mừng! Bạn đã học được cách sử dụng Java Comparators từ trong ra ngoài. Từ việc sắp xếp các chuỗi đơn giản đến các đối tượng phức tạp, bạn bây giờ có khả năng tổ chức dữ liệu theo bất kỳ cách nào bạn chọn. Nhớ rằng, thực hành làm nên hoàn hảo, vì vậy đừng ngần ngại thử nghiệm với các tiêu chí sắp xếp khác nhau và các đối tượng khác nhau.

Khi bạn tiếp tục hành trình Java của mình, bạn sẽ thấy Comparators là những công cụ vô giá trong bộ công cụ lập trình của bạn. Chúng không chỉ được sử dụng để sắp xếp danh sách - chúng còn được sử dụng trong nhiều bộ sưu tập và thuật toán Java để duy trì thứ tự và thực hiện các tìm kiếm hiệu quả.

Tiếp tục lập mã, tiếp tục học hỏi, và quan trọng nhất, hãy vui vẻ! Ai biết được, có lẽ một ngày nào đó bạn sẽ viết một thuật toán sắp xếp thay đổi cách chúng ta tổ chức dữ liệu. Đến那时, chúc may mắn! ??‍??‍?

Các phương thức của giao diện Comparator

Dưới đây là bảng các phương thức chính trong giao diện Comparator:

Phương thức Mô tả
compare(T o1, T o2) So sánh hai đối số cho thứ tự.
equals(Object obj) Chỉ ra xem một đối tượng khác có "bằng" comparator này hay không.
reversed() Trả về một comparator đảo ngược thứ tự của comparator này.
thenComparing(Comparator<? super T> other) Trả về một comparator lexicographic với một comparator khác.
thenComparingInt(ToIntFunction<? super T> keyExtractor) Trả về một comparator lexicographic với một hàm trích xuất khóa nguyên.
thenComparingLong(ToLongFunction<? super T> keyExtractor) Trả về một comparator lexicographic với một hàm trích xuất khóa dài.
thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) Trả về một comparator lexicographic với một hàm trích xuất khóa double.

Các phương thức này cung cấp các công cụ mạnh mẽ để tạo logic sắp xếp phức tạp, cho phép bạn xây dựng các comparator phức tạp cho bất kỳ loại dữ liệu nào hoặc yêu cầu sắp xếp nào.

Credits: Image by storyset