Comparator Java: Sắp xếp dữ liệu như một DJ chuyên nghiệp
Java – OOP

Comparator Java: Sắp xếp dữ liệu như một DJ chuyên nghiệp

Author

Admin System

@root

Ngày xuất bản

22 Mar, 2026

Lượt xem

1 Lượt

Comparator interface

Các bạn Gen Z thân mến! Đã bao giờ các bạn lướt TikTok, thấy feed nó cứ nhảy loạn xạ không theo ý mình chưa? Hay tìm bài hát trên Spotify mà muốn sắp xếp theo đủ kiểu: từ nghệ sĩ, album, đến số lượt nghe? Đó chính là lúc chúng ta cần một "phù thủy" sắp xếp, một "DJ" chuyên nghiệp để khuấy động và đưa mọi thứ vào đúng trật tự. Trong Java, phù thủy đó chính là Comparator interface.

Comparator là gì mà "ghê gớm" vậy anh Creyt?

Tưởng tượng thế này, mỗi object trong Java của chúng ta như một người trong một buổi tiệc. Comparable là khi mỗi người tự biết số thứ tự của mình (ví dụ: số ID trên thẻ sinh viên). Nhưng nếu anh Creyt muốn xếp hàng theo chiều cao, hay theo màu áo, hay theo ai có nhiều 'streak' nhất trên Snapchat? Lúc đó, cái 'số thứ tự' tự thân nó không còn đủ nữa. Chúng ta cần một 'trọng tài' bên ngoài, một 'người chấm điểm' để đưa ra tiêu chí sắp xếp. Comparator chính là cái 'trọng tài' đó!

Nó là một interface trong gói java.util, chỉ có một "nhiệm vụ" duy nhất: định nghĩa cách so sánh hai đối tượng. Cái "nhiệm vụ" đó được thể hiện qua phương thức:

int compare(T o1, T o2)

Mẹo nhỏ để nhớ giá trị trả về:

  • negative integer (số âm): Nếu o1 "nhỏ hơn" o2.
  • zero (số 0): Nếu o1 "bằng" o2.
  • positive integer (số dương): Nếu o1 "lớn hơn" o2.

Đơn giản như nhìn điểm số thôi! o1 mà điểm thấp hơn o2 thì trả về âm, bằng thì 0, cao hơn thì dương. Dễ hiểu đúng không?

Khác biệt cốt lõi với Comparable (mà anh Creyt hay gọi là "natural ordering" – sắp xếp tự nhiên):

  • Comparable: Object tự biết cách sắp xếp mình (như mang theo ID card cá nhân). Bạn implement nó bên trong class của đối tượng.
  • Comparator: Một bên thứ ba đưa ra luật chơi để so sánh hai object bất kỳ. Nó hoạt động bên ngoài class của đối tượng.

Tại sao chúng ta cần "trọng tài" Comparator?

  1. Khi object của bạn không có "sắp xếp tự nhiên": Kiểu như một người không có số ID, bạn phải tự định nghĩa cách so sánh họ.
  2. Khi bạn muốn sắp xếp một object theo NHIỀU TIÊU CHÍ khác nhau: Một object chỉ có thể implement Comparable một lần (chỉ có một cách sắp xếp tự nhiên). Nhưng nó có thể được sắp xếp bởi HÀNG TRĂM Comparator khác nhau! Lúc thì theo tuổi, lúc thì theo điểm GPA, lúc lại theo tên... Comparator cân tất!
  3. Khi bạn làm việc với thư viện của người khác và không thể sửa đổi class của họ: Bạn không thể thêm implements Comparable vào một class mà bạn không sở hữu mã nguồn. Comparator là "phao cứu sinh" trong tình huống này, cho phép bạn định nghĩa cách sắp xếp mà không cần chạm vào class gốc.
Illustration

Code Ví Dụ Minh Họa: "DJ" Comparator thực chiến!

Giả sử chúng ta có một lớp Student và muốn sắp xếp danh sách sinh viên này theo nhiều tiêu chí khác nhau.

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

// Lớp Student - Đối tượng mà chúng ta muốn sắp xếp
class Student {
    String name;
    int age;
    double gpa;

    public Student(String name, int age, double gpa) {
        this.name = name;
        this.age = age;
        this.gpa = gpa;
    }

    // Getters cho các thuộc tính (cần thiết để Comparator truy cập)
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getGpa() { return gpa; }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", gpa=" + gpa + "}";
    }
}

public class ComparatorDemo {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("An", 20, 3.5));
        students.add(new Student("Binh", 22, 3.8));
        students.add(new Student("Long", 20, 3.2));
        students.add(new Student("Chi", 21, 3.9));
        students.add(new Student("An", 21, 3.7)); // Tên trùng, tuổi/gpa khác

        System.out.println("Danh sách sinh viên ban đầu:");
        students.forEach(System.out::println);

        // --- Ví dụ 1: Sắp xếp theo tuổi (tăng dần) dùng Anonymous Inner Class ---
        // Đây là kiểu 'cổ điển' chút, nhưng vẫn rất quan trọng để hiểu bản chất.
        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                return Integer.compare(s1.getAge(), s2.getAge());
            }
        });
        System.out.println("\n--- Sắp xếp theo tuổi (tăng dần): ---");
        students.forEach(System.out::println);

        // --- Ví dụ 2: Sắp xếp theo GPA (giảm dần) dùng Lambda Expression ---
        // Các bạn Gen Z thích sự gọn lẹ đúng không? Lambda chính là chân ái!
        // Lưu ý: List.sort() là phương thức mặc định của List từ Java 8, tiện hơn Collections.sort().
        students.sort((s1, s2) -> Double.compare(s2.getGpa(), s1.getGpa())); // s2 vs s1 để giảm dần
        System.out.println("\n--- Sắp xếp theo GPA (giảm dần): ---");
        students.forEach(System.out::println);

        // --- Ví dụ 3: Sắp xếp theo Tên, nếu tên giống nhau thì theo Tuổi ---
        // Dùng Comparator.comparing và thenComparing - cách anh Creyt khuyến khích nhất!
        Comparator<Student> byNameThenByAge = Comparator
            .comparing(Student::getName) // Sắp xếp theo tên (tăng dần mặc định)
            .thenComparing(Student::getAge); // Nếu tên giống nhau, sắp xếp theo tuổi (tăng dần)

        students.sort(byNameThenByAge);
        System.out.println("\n--- Sắp xếp theo Tên, sau đó theo Tuổi: ---");
        students.forEach(System.out::println);

        // --- Ví dụ 4: Sắp xếp phức tạp hơn ---
        // Theo GPA giảm dần, nếu GPA giống nhau thì theo tên tăng dần, nếu tên giống nhau thì theo tuổi tăng dần
        Comparator<Student> complexSort = Comparator
            .comparing(Student::getGpa, Comparator.reverseOrder()) // GPA giảm dần
            .thenComparing(Student::getName) // Tên tăng dần
            .thenComparing(Student::getAge); // Tuổi tăng dần

        students.sort(complexSort);
        System.out.println("\n--- Sắp xếp phức tạp (GPA giảm, Tên tăng, Tuổi tăng): ---");
        students.forEach(System.out::println);
    }
}

Mẹo "xịn xò" từ anh Creyt để dùng Comparator hiệu quả

  • Dùng Lambda Expression: Các bạn Gen Z thích sự gọn gàng, nhanh chóng đúng không? Lambda chính là chân ái! Thay vì viết cả một new Comparator<Student>() { ... }, chỉ cần (s1, s2) -> ... là xong. Đẹp mắt, dễ đọc, lại còn ngắn gọn.
  • Comparator.comparing()thenComparing(): Đây là "combo" thần thánh để tạo ra các Comparator phức tạp mà không cần viết quá nhiều logic. Nó giúp code của bạn "clean" như phòng trọ mới dọn vậy. Cứ comparing một tiêu chí, rồi thenComparing các tiêu chí phụ. Quá tiện!
  • Xử lý null: Nếu dữ liệu của bạn có thể có null, hãy cẩn thận! Comparator.nullsFirst() hoặc nullsLast() là trợ thủ đắc lực để đảm bảo null được đặt ở đầu hoặc cuối danh sách mà không gây NullPointerException.
  • Hiểu rõ sự khác biệt: Comparable là "bên trong" object, Comparator là "bên ngoài". Cứ nhớ thế là không bao giờ nhầm!

Ứng dụng thực tế của "DJ" Comparator trong thế giới số

Comparator không phải là thứ gì đó "trên trời" đâu, nó xuất hiện khắp mọi nơi trong cuộc sống số của chúng ta:

  • Sàn thương mại điện tử (Shopee, Tiki, Lazada): Khi bạn tìm "điện thoại", bạn có thể sắp xếp theo "Giá từ thấp đến cao", "Giá từ cao đến thấp", "Mới nhất", "Bán chạy nhất", "Đánh giá cao nhất". Mỗi tiêu chí đó là một Comparator đang hoạt động ngầm đó!
  • Mạng xã hội (Facebook, Instagram, TikTok): Feed của bạn không chỉ sắp xếp theo thời gian mà còn theo mức độ tương tác, độ liên quan với bạn. Đó là những Comparator siêu phức tạp được các thuật toán áp dụng.
  • Game (Leaderboard): Bảng xếp hạng người chơi thường sắp xếp theo điểm số, thời gian hoàn thành, cấp độ. Mỗi cách sắp xếp là một Comparator riêng biệt.
  • Hệ điều hành (Windows Explorer, Finder): Khi bạn sắp xếp file theo tên, ngày tạo, kích thước, loại file. Tất cả đều là Comparator.

Khi nào nên dùng và khi nào "thôi thôi để đấy"?

Anh Creyt đã từng "lạm dụng" Comparator khi mới học, nhưng sau này mới biết "liệu cơm gắp mắm" là quan trọng. Vậy khi nào nên dùng?

  • Nên dùng Comparator khi:

    • Bạn cần sắp xếp một collection theo nhiều cách khác nhau (ví dụ: một danh sách sinh viên lúc cần theo tên, lúc cần theo tuổi, lúc cần theo GPA).
    • Bạn không thể (hoặc không muốn) thay đổi class của đối tượng để implement Comparable. Hay nói cách khác, bạn cần "ngoại lực" để sắp xếp.
    • Khi Comparable (sắp xếp tự nhiên) của đối tượng không phù hợp với yêu cầu của bạn tại một thời điểm cụ thể.
  • Không nên lạm dụng Comparator khi:

    • Nếu một object luôn luôn có một cách sắp xếp mặc định và bạn không bao giờ cần sắp xếp theo cách khác, hãy để nó implement Comparable cho gọn. Đừng biến mọi thứ thành phức tạp không cần thiết. Đôi khi, sự đơn giản là tốt nhất!

Hãy tự thử nghiệm tạo ra các Comparator khác nhau cho class Student của anh Creyt. Sắp xếp theo tên ngược, theo tuổi giảm dần, hoặc kết hợp nhiều tiêu chí. Đó là cách tốt nhất để "thấm" kiến thức này!

Thuộc Series: Java – OOP

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!