Trang chủHướng dẫnGiải mã Comparable và Comparator trong Java qua ví dụ minh họa

Giải mã Comparable và Comparator trong Java qua ví dụ minh họa

CyStack blog 8 phút để đọc
CyStack blog13/06/2025
Locker Avatar

Bao Tran

Web Developer

Locker logo social
Reading Time: 8 minutes

Comparable và Comparator trong Java đóng vai trò quan trọng giúp sắp xếp các tập hợp đối tượng. Java cung cấp một số phương thức dựng sẵn để sắp xếp mảng các kiểu dữ liệu nguyên thủy (primitive types) và mảng hoặc danh sách các lớp bao (Wrapper classes).

Comparable và Comparator trong Java

Trong bài viết này, cùng tìm hiểu cách sắp xếp các mảng hoặc danh sách của các kiểu nguyên thủy và các lớp bao, sau đó mở rộng sang sử dụng giao diện java.lang.Comparablejava.util.Comparator để sắp xếp các đối tượng của lớp tùy chỉnh. Hãy theo dõi một ví dụ đơn giản dưới đây.

package com.journaldev.sort;

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

public class JavaObjectSorting {

    /**
     * This class shows how to sort primitive arrays,
     * Wrapper classes Object Arrays
     * @param args
     */
    public static void main(String[] args) {
        //sort primitives array like int array
        int[] intArr = {5,9,1,10};
        Arrays.sort(intArr);
        System.out.println(Arrays.toString(intArr));

        //sorting String array
        String[] strArr = {"A", "C", "B", "Z", "E"};
        Arrays.sort(strArr);
        System.out.println(Arrays.toString(strArr));

        //sorting list of objects of Wrapper classes
        List<String> strList = new ArrayList<String>();
        strList.add("A");
        strList.add("C");
        strList.add("B");
        strList.add("Z");
        strList.add("E");
        Collections.sort(strList);
        for(String str: strList) System.out.print(" "+str);
    }
}

Java cho phép sắp xếp các kiểu dữ liệu như int[], double[] hoặc String[] bằng cách sử dụng các phương thức có sẵn trong lớp Arrays hoặc Collections. Kết quả cho ra các phần tử đã được sắp xếp theo thứ tự tăng dần:

[1, 5, 9, 10]
[A, B, C, E, Z]
 A B C E Z

Bây giờ hãy thử sắp xếp một mảng các đối tượng.

package com.journaldev.sort;

public class Employee {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    //this is overridden to print the user-friendly information about the Employee
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

}

Đây là mã code dùng để sắp xếp mảng chứa các đối tượng tùy chỉnh như Employee.

//sorting object array
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);

//sorting employees array using Comparable interface implementation
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\\n"+Arrays.toString(empArr));

Khi ta thử chạy lệnh này, nó đưa ra một ngoại lệ tại thời điểm chạy như sau.

Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
	at java.util.Arrays.sort(Arrays.java:472)
	at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)

Comparable và Comparator trong Java

Java Comparable

Java cung cấp giao diện Comparable, loại giao diện cần được triển khai bởi lớp tùy chỉnh bất kỳ nếu bạn muốn sử dụng các hàm sắp xếp Array hoặc Collections. Các hàm sắp xếp trong giao diện Comparable sử dụng phương thức compareTo(T obj), bạn có thể kiểm tra bất kỳ lớp Wrapper, String hoặc Date nào để xác nhận điều này.

Bạn nên triển khai phương thức này sao cho kết quả trả về một số nguyên âm, số không hoặc số nguyên dương khi đối tượng “này” nhỏ hơn, bằng hoặc lớn hơn đối tượng “kia” được truyền dưới dạng đối số.

Sau khi triển khai giao diện Comparable trong lớp Employee, đây là kết quả.

package com.journaldev.sort;

import java.util.Comparator;

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee emp) {
        //let's sort the employee based on an id in ascending order
        //returns a negative integer, zero, or a positive integer as this employee id
        //is less than, equal to, or greater than the specified object.
        return (this.id - emp.id);
    }

    @Override
    //this is required to print the user-friendly information about the Employee
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

}

Giờ hãy thực thi đoạn mã trên để sắp xếp mảng Employee và trả kết quả.

Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]

Vấn đề của Java Comparator

Như bạn có thể thấy, mảng Employees được sắp xếp theo id thứ tự tăng dần. Tuy nhiên, trong hầu hết các tình huống thực tế, chúng ta thường muốn sắp xếp dựa trên các tham số khác nhau. Ví dụ, với vai trò là một CEO, tôi muốn sắp xếp nhân viên dựa trên mức lương, nhưng một HR lại muốn sắp xếp dựa trên độ tuổi.

Đây là tình huống mà chúng ta cần sử dụng giao diện Java Comparator bởi vì phương thức Comparable.compareTo(Object o) chỉ hỗ trợ một cách sắp xếp mặc định, ta không thể thay đổi cách sắp xếp một cách linh hoạt. Trong khi đó, với Comparator, chúng ta có thể định nghĩa nhiều phương thức với nhiều cách sắp xếp khác nhau, sau đó chọn phương thức sắp xếp phù hợp với yêu cầu của mình.

Java Comparator

Comparator là một giao diện nằm trong gói java.util, cho phép bạn xác định nhiều cách sắp xếp khác nhau mà không cần chỉnh sửa lớp đối tượng gốc. Giao diện này triển khai phương thức compare(Object o1, Object o2), lấy hai đối số Object, và trả về:

  • Số nguyên âm nếu o1 < o2
  • 0 nếu o1 = o2
  • Số nguyên dương nếu o1 > o2

Giao diện Comparable và Comparator sử dụng Generics để kiểm tra tại thời điểm biên dịch. Sau đây là cách bạn có thể tạo ra các chương trình triển khai Comparator khác nhau trong lớp Employee.

/**
     * Comparator to sort employees list or array in order of Salary
     */
    public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return (int) (e1.getSalary() - e2.getSalary());
        }
    };

    /**
     * Comparator to sort employees list or array in order of Age
     */
    public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getAge() - e2.getAge();
        }
    };

    /**
     * Comparator to sort employees list or array in order of Name
     */
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };

Tất cả các chương trình triển khai trên của giao diện Comparator đều là các lớp ẩn danh. Bạn có thể sử dụng các bộ so sánh này để truyền đối số cho hàm sắp xếp của các lớp Array và Collections.

//sort employees array using Comparator by Salary
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\\n"+Arrays.toString(empArr));

//sort employees array using Comparator by Age
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\\n"+Arrays.toString(empArr));

//sort employees array using Comparator by Name
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\\n"+Arrays.toString(empArr));

Dưới đây là kết quả của đoạn mã trên:

Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]

Vậy là giờ chúng ta đã biết rằng nếu muốn sắp xếp mảng hoặc danh sách các đối tượng trong Java, ta cần triển khai giao diện Java Comparable để cung cấp sắp xếp mặc định và triển khai giao diện Java Comparator để cung cấp các cách sắp xếp khác nhau. Ta cũng có thể tạo các lớp riêng biệt để triển khai giao diện Comparator và sau đó sử dụng nó. Dưới đây là các lớp cuối cùng để minh họa về Comparable và Comparator trong Java.

package com.journaldev.sort;

import java.util.Comparator;

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public long getSalary() {
        return salary;
    }

    public Employee(int id, String name, int age, int salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee emp) {
        //let's sort the employee based on an id in ascending order
        //returns a negative integer, zero, or a positive integer as this employee id
        //is less than, equal to, or greater than the specified object.
        return (this.id - emp.id);
    }

    @Override
    //this is required to print the user-friendly information about the Employee
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

    /**
     * Comparator to sort employees list or array in order of Salary
     */
    public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return (int) (e1.getSalary() - e2.getSalary());
        }
    };

    /**
     * Comparator to sort employees list or array in order of Age
     */
    public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getAge() - e2.getAge();
        }
    };

    /**
     * Comparator to sort employees list or array in order of Name
     */
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {

        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
}

Đây là lớp triển khai riêng biệt của giao diện Comparator, sẽ so sánh hai đối tượng Employees dựa trên id của chúng và nếu trả kết quả giống nhau thì sẽ tiếp tục so sánh dựa trên tên.

package com.journaldev.sort;

import java.util.Comparator;

public class EmployeeComparatorByIdAndName implements Comparator<Employee> {

    @Override
    public int compare(Employee o1, Employee o2) {
        int flag = o1.getId() - o2.getId();
        if(flag==0) flag = o1.getName().compareTo(o2.getName());
        return flag;
    }

}

Đây là lớp kiểm tra đã sử dụng nhiều cách khác nhau để sắp xếp các đối tượng trong Java bằng cách sử dụng Comparable và Comparator.

package com.journaldev.sort;

import java.util.Arrays;

public class JavaObjectSorting {

    /**
     * This class shows how to sort custom objects array/list
     * implementing Comparable and Comparator interfaces
     * @param args
     */
    public static void main(String[] args) {

        //sorting custom object array
        Employee[] empArr = new Employee[4];
        empArr[0] = new Employee(10, "Mikey", 25, 10000);
        empArr[1] = new Employee(20, "Arun", 29, 20000);
        empArr[2] = new Employee(5, "Lisa", 35, 5000);
        empArr[3] = new Employee(1, "Pankaj", 32, 50000);

        //sorting employees array using Comparable interface implementation
        Arrays.sort(empArr);
        System.out.println("Default Sorting of Employees list:\\n"+Arrays.toString(empArr));

        //sort employees array using Comparator by Salary
        Arrays.sort(empArr, Employee.SalaryComparator);
        System.out.println("Employees list sorted by Salary:\\n"+Arrays.toString(empArr));

        //sort employees array using Comparator by Age
        Arrays.sort(empArr, Employee.AgeComparator);
        System.out.println("Employees list sorted by Age:\\n"+Arrays.toString(empArr));

        //sort employees array using Comparator by Name
        Arrays.sort(empArr, Employee.NameComparator);
        System.out.println("Employees list sorted by Name:\\n"+Arrays.toString(empArr));

        //Employees list sorted by ID and then name using Comparator class
        empArr[0] = new Employee(1, "Mikey", 25, 10000);
        Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
        System.out.println("Employees list sorted by ID and Name:\\n"+Arrays.toString(empArr));
    }

}

Đây là kết quả cho chương trình trên:

Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]

Tóm lại, ta thấy java.lang.Comparablejava.util.Comparator là các giao diện mạnh mẽ có thể được sử dụng để thực thi chức năng sắp xếp các đối tượng trong Java.

So sánh Comparable và Comparator

Tiêu chí Comparable Comparator
Số cách sắp xếp hỗ trợ Một cách duy nhất Nhiều cách khác nhau
Thay đổi lớp chính Cần triển khai và ghi đè compareTo Không cần thay đổi lớp chính
Gói java.lang java.util
Phương thức chính compareTo(T obj) compare(T o1, T o2)
Cách sử dụng Không cần thay đổi gì ở phía client. Dùng mặc định với Arrays.sort() hoặc Collections.sort(), tự động sử dụng phương thức compareTo() của lớp Máy khách cần cung cấp lớp Comparator để sử dụng phương thức compare()

Giao diện Comparable và Comparator trong Java là công cụ cực kỳ mạnh mẽ để sắp xếp các đối tượng một cách linh hoạt và hiệu quả. Việc hiểu rõ sự khác biệt và biết cách sử dụng chúng đúng lúc sẽ giúp bạn xây dựng các ứng dụng Java có cấu trúc tốt, dễ bảo trì và đáp ứng tốt các yêu cầu thực tế về xử lý dữ liệu.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất