Interface Comparator<T>
Interface Comparator<T> nằm trong gói java.util, được sử dụng để so sánh các đối tượng của một lớp do người dùng tự định nghĩa, cho phép bạn xác định thứ tự sắp xếp các phần tử. Khác với Comparable, chỉ cung cấp một thứ tự tự nhiên duy nhất, Comparator cho phép bạn tạo nhiều cách sắp xếp dựa trên các tiêu chí khác nhau, ví dụ như sắp xếp theo mã số, tên, tuổi hoặc các thuộc tính khác.
Các đặc điểm chính về Interface Comparator<T>
↳ So sánh hai đối tượng: Comparator cung cấp phương thức compare(T o1, T o2) để so sánh hai đối tượng o1 và o2. Phương thức này trả về một số nguyên, có ý nghĩa như sau:
↳ Số nguyên âm nếu o1 nhỏ hơn o2.
↳ Số 0 nếu o1 bằng o2.
↳ Số nguyên dương nếu o1 lớn hơn o2.
↳ Không yêu cầu thứ tự tự nhiên: Không giống như Comparable, Comparator không yêu cầu đối tượng phải có thứ tự tự nhiên. Bạn có thể tạo ra nhiều Comparator khác nhau để so sánh cùng một kiểu đối tượng theo nhiều tiêu chí khác nhau.
↳ Sử dụng trong sắp xếp: Comparator thường được sử dụng khi bạn muốn sắp xếp các phần tử trong một List, Set, hoặc Map theo một thứ tự cụ thể. Bạn có thể truyền một Comparator vào các phương thức sắp xếp như Collections.sort() hoặc Arrays.sort().
↳ Quản lý null: Không giống như Comparable, Comparator có thể xử lý các đối số null, điều này hữu ích trong các trường hợp đặc biệt khi bạn cần so sánh các đối tượng có thể là null.
↳ Tương thích với equals: Khi một Comparator được coi là "tương thích với equals", điều đó có nghĩa là nếu Comparator so sánh hai đối tượng và xác định chúng bằng nhau (nghĩa là compare(e1, e2) == 0), thì phương thức equals của hai đối tượng này cũng phải trả về true khi so sánh chúng (e1.equals(e2) == true). Nói cách khác, nếu hai đối tượng được coi là giống nhau theo cách sắp xếp mà Comparator định nghĩa, thì chúng cũng phải được coi là giống nhau theo phương thức equals.
Khai báo Interface Comparator<T> trong Java
Để sử dụng Interface Comparator<T> bạn cần import gói java.util vào đầu file Java của mình.
Cú pháp câu lệnh import:
Cú pháp
import java.util.Comparator;
Cú pháp khai báo Interface Comparator<T>:
Cú pháp
@FunctionalInterface
public interface Comparator<T>
Dưới đây là giải thích chi tiết về cú pháp khai báo này:
↳ Functional Interface: Comparator<T> là một functional interface. Điều này có nghĩa là giao diện này chỉ chứa đúng một phương thức trừu tượng (abstract method). Các functional interface thường được sử dụng để hỗ trợ các biểu thức lambda trong Java. Vì Comparator<T> chỉ có một phương thức trừu tượng là compare(T o1, T o2), nên nó được đánh dấu bằng @FunctionalInterface. Chú thích @FunctionalInterface không bắt buộc phải sử dụng, nhưng nó giúp đảm bảo rằng giao diện chỉ có một phương thức trừu tượng, giúp tránh lỗi lập trình và làm rõ mục đích của giao diện.
↳ Comparator<T>: Đây là một giao diện trong gói java.util, được sử dụng để so sánh hai đối tượng. Nó giúp bạn định nghĩa cách sắp xếp các đối tượng của một lớp cụ thể. Giao diện này cung cấp một phương thức trừu tượng chính là compare(T o1, T o2), giúp bạn so sánh hai đối tượng và xác định xem đối tượng nào lớn hơn, nhỏ hơn hoặc bằng nhau.
Các phương thức chính trong Interface Comparator
Giao diện Comparator cung cấp các phương thức để tạo và kết hợp các comparator để sắp xếp các đối tượng theo các tiêu chí khác nhau. Dưới đây là danh sách tất cả các phương thức của Interface Comparator<T> trong Java:
↳ int compare(T o1, T o2): So sánh hai đối số của nó theo thứ tự.
↳ static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor): Chấp nhận một hàm trích xuất một khóa sắp xếp Comparable từ một kiểu T và trả về một Comparator<T> so sánh theo khóa sắp xếp đó.
↳ static <T,U> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator): Chấp nhận một hàm trích xuất một khóa sắp xếp từ một kiểu T và trả về một Comparator<T> so sánh theo khóa sắp xếp đó bằng cách sử dụng Comparator đã cho.
↳ static <T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor): Chấp nhận một hàm trích xuất một khóa sắp xếp double từ một kiểu T và trả về một Comparator<T> so sánh theo khóa sắp xếp đó.
↳ static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor): Chấp nhận một hàm trích xuất một khóa sắp xếp int từ một kiểu T và trả về một Comparator<T> so sánh theo khóa sắp xếp đó.
↳ static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor): Chấp nhận một hàm trích xuất một khóa sắp xếp long từ một kiểu T và trả về một Comparator<T> so sánh theo khóa sắp xếp đó.
↳ boolean equals(Object obj): Chỉ ra xem một đối tượng khác có "bằng" với comparator này hay không.
↳ static <T extends Comparable<? super T>> Comparator<T> naturalOrder(): Trả về một comparator so sánh các đối tượng Comparable theo thứ tự tự nhiên.
↳ static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator): Trả về một comparator thân thiện với null xem null nhỏ hơn không null.
↳ static <T> Comparator<T> nullsLast(Comparator<? super T> comparator): Trả về một comparator thân thiện với null xem null lớn hơn không null.
↳ default Comparator<T> reversed(): Trả về một comparator áp đặt thứ tự ngược của comparator này.
↳ static <T extends Comparable<? super T>> Comparator<T> reverseOrder(): Trả về một comparator áp đặt thứ tự ngược của thứ tự tự nhiên.
↳ default Comparator<T> thenComparing(Comparator<? super T> other): Trả về một comparator theo thứ tự từ điển với một comparator khác.
↳ Default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T,? extends U> keyExtractor): Trả về một comparator theo thứ tự từ điển với một hàm trích xuất một khóa sắp xếp Comparable.
↳ default <U> Comparator<T>: thenComparing(Function<? super T,? extends U> keyExtractor, Comparator<? super U> keyComparator): Trả về một comparator theo thứ tự từ điển với một hàm trích xuất một khóa để so sánh với Comparator đã cho.
↳ default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor): Trả về một comparator theo thứ tự từ điển với một hàm trích xuất một khóa sắp xếp double.
↳ default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor): Trả về một comparator theo thứ tự từ điển với một hàm trích xuất một khóa sắp xếp int.
↳ default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor): Trả về một comparator theo thứ tự từ điển với một hàm trích xuất một khóa sắp xếp long.
Ví dụ đơn giản về sử dụng Comparator
Dưới đây là một ví dụ về cách bạn có thể sử dụng giao diện Comparator để sắp xếp các đối tượng theo các tiêu chí khác nhau.
Các lớp ví dụ:
↳ Student.java: Lớp đại diện cho một sinh viên với các thuộc tính như tên và tuổi.
↳ AgeComparator.java: Lớp triển khai Comparator để so sánh các đối tượng Student theo tuổi.
↳ NameComparator.java: Lớp triển khai Comparator để so sánh các đối tượng Student theo tên.
↳ Example.java: Lớp minh họa cách sử dụng các bộ so sánh trên để sắp xếp danh sách sinh viên.
Ví dụ: Example.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Student {
int ID;
String name;
int age;
Student(int ID, String name, int age) {
this.ID = ID;
this.name = name;
this.age = age;
}
}
class AgeComparator implements Comparator<Student> {
public int compare(Student s1, Student s2) {
return Integer.compare(s1.age, s2.age);
}
}
class NameComparator implements Comparator<Student> {
public int compare(Student s1, Student s2) {
return s1.name.compareTo(s2.name);
}
}
public class Example {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student(22, "A", 23));
list.add(new Student(100, "Z", 27));
list.add(new Student(133, "L", 21));
// Sắp xếp theo tuổi
Collections.sort(list, new AgeComparator());
System.out.println("Sắp xếp theo Tuổi:");
for (Student s : list) {
System.out.println(s.ID + " " + s.name + " " + s.age);
}
// Sắp xếp theo tên
Collections.sort(list, new NameComparator());
System.out.println("Sắp xếp theo Tên:");
for (Student s : list) {
System.out.println(s.ID + " " + s.name + " " + s.age);
}
}
}
Kết quả của chương trình là:
133 L 21
22 A 23
100 Z 27
Sắp xếp theo Tên:
22 A 23
133 L 21
100 Z 27
Giao diện Comparator là một công cụ linh hoạt trong Java, cho phép bạn định nghĩa thứ tự tùy chỉnh để sắp xếp các đối tượng dựa trên các thuộc tính khác nhau. Nó hoạt động tốt với lớp Collections để sắp xếp danh sách và với các cấu trúc dữ liệu như TreeSet và TreeMap yêu cầu các phần tử được sắp xếp.
Ví dụ sử dụng Comparator với biểu thức Lambda
Giả sử bạn có lớp Person và bạn muốn sắp xếp danh sách Person theo tên hoặc tuổi mà không cần phải thay đổi lớp Person hoặc thực hiện giao diện Comparable.
Ví dụ: Example.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Person {
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter cho tên
public String getName() {
return name;
}
// Getter cho tuổi
public int getAge() {
return age;
}
@Override
public String toString() {
return name + " (" + age + " tuổi)";
}
}
public class Example {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("A", 30));
people.add(new Person("Ch", 35));
people.add(new Person("B", 25));
// Sắp xếp theo tên sử dụng biểu thức lambda
Collections.sort(people, (Person p1, Person p2) -> p1.getName().compareTo(p2.getName()));
System.out.println("Danh sách sắp xếp theo tên:");
for (Person person : people) {
System.out.println(person);
}
// Sắp xếp theo tuổi sử dụng biểu thức lambda
Collections.sort(people, (Person p1, Person p2) -> Integer.compare(p1.getAge(), p2.getAge()));
System.out.println("\nDanh sách sắp xếp theo tuổi:");
for (Person person : people) {
System.out.println(person);
}
}
}
Kết quả của chương trình là:
A (30 tuổi)
B (25 tuổi)
Ch (35 tuổi)
Danh sách sắp xếp theo tuổi:
B (25 tuổi)
A (30 tuổi)
Ch (35 tuổi)
Comparator với biểu thức lambda là cách linh hoạt để sắp xếp các đối tượng theo các tiêu chí khác nhau mà không cần phải thay đổi lớp đối tượng. Nó cho phép bạn dễ dàng định nghĩa các tiêu chí sắp xếp tùy chỉnh trong khi vẫn giữ mã nguồn gọn gàng và dễ đọc.
Xử lý null trong Comparator
Comparator cung cấp cách xử lý null dễ dàng hơn so với Comparable. Khi sử dụng Comparator, bạn có thể sử dụng các tiện ích như Comparator.nullsFirst() hoặc Comparator.nullsLast() để định nghĩa rõ ràng cách xử lý null. Đây là cách đơn giản và hiệu quả để đảm bảo rằng các giá trị null được xử lý theo cách mà bạn mong muốn, mà không cần phải viết mã xử lý null thủ công.
Dưới đây là cách sử dụng Comparator.nullsFirst() và Comparator.nullsLast() trong ví dụ với lớp Student:
Ví dụ: Example.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
// Lớp Student với thuộc tính name và age
class Student {
private String name;
private Integer age;
// Constructor để khởi tạo đối tượng Student
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
// Getter cho thuộc tính age
public Integer getAge() {
return age;
}
// Override phương thức toString để hiển thị thông tin đối tượng
@Override
public String toString() {
return name + " (" + (age == null ? "null" : age) + " tuổi)";
}
public static void main(String[] args) {
// Tạo danh sách các đối tượng Student
List<Student> students = new ArrayList<>();
students.add(new Student("Ai", 25));
students.add(new Student("Bo", null));
students.add(new Student("Char", 22));
students.add(new Student("Dav", null));
// Bộ so sánh để xử lý các giá trị null, đặt giá trị null ở cuối
Comparator<Student> ageComparator = Comparator.comparing(
Student::getAge, // Trích xuất thuộc tính age để so sánh
Comparator.nullsLast(Comparator.naturalOrder()) // Đặt giá trị null ở cuối khi sắp xếp
);
// Sắp xếp danh sách bằng bộ so sánh ageComparator
Collections.sort(students, ageComparator);
// Hiển thị kết quả sau khi sắp xếp với nullsLast
System.out.println("Danh sách sau khi sắp xếp với nullsLast:");
for (Student student : students) {
System.out.println(student);
}
// Bộ so sánh để xử lý các giá trị null, đặt giá trị null ở đầu
Comparator<Student> ageComparator1 = Comparator.comparing(
Student::getAge, // Trích xuất thuộc tính age để so sánh
Comparator.nullsFirst(Comparator.naturalOrder()) // Đặt giá trị null ở đầu khi sắp xếp
);
// Sắp xếp danh sách bằng bộ so sánh ageComparator1
Collections.sort(students, ageComparator1);
// Hiển thị kết quả sau khi sắp xếp với nullsFirst
System.out.println("Danh sách sau khi sắp xếp với nullsFirst:");
for (Student student : students) {
System.out.println(student);
}
}
}
Kết quả của chương trình là:
Char (22 tuổi)
Ai (25 tuổi)
Bo (null tuổi)
Dav (null tuổi)
Danh sách sau khi sắp xếp với nullsFirst:
Bo (null tuổi)
Dav (null tuổi)
Char (22 tuổi)
Ai (25 tuổi)
Giải thích:
↳ Comparator.nullsLast(Comparator<? super T> comparator): Comparator.nullsLast() tạo một Comparator mà sẽ coi các giá trị null là lớn hơn tất cả các giá trị không null. Điều này có nghĩa là các giá trị null sẽ được đặt ở cuối danh sách khi sắp xếp theo thứ tự tăng dần.
↳ Comparator.nullsFirst(Comparator<? super T> comparator): Comparator.nullsFirst() tạo một Comparator mà sẽ coi các giá trị null là nhỏ hơn tất cả các giá trị không null. Điều này có nghĩa là các giá trị null sẽ được đặt ở đầu danh sách khi sắp xếp theo thứ tự tăng dần.
↳ Comparator.naturalOrder(): Comparator.naturalOrder() là một Comparator đã được định nghĩa sẵn, thực hiện so sánh theo thứ tự tự nhiên của các giá trị. Trong trường hợp này, chúng ta sử dụng nó để so sánh các giá trị age của các đối tượng Student.
Hy vọng rằng ví dụ này giúp bạn hiểu cách sử dụng Comparator.nullsFirst() và Comparator.nullsLast() để xử lý các giá trị null trong các phép so sánh!
So sánh Interface Comparable và Interface Comparator
Trong Java, cả Comparable và Comparator đều được sử dụng để so sánh các đối tượng. Tuy nhiên, chúng có những cách tiếp cận khác nhau và phù hợp với các tình huống khác nhau.
| Tiêu Chí | Comparable | Comparator |
|---|---|---|
| Gói (Package) | java.lang | java.util |
| Phương Thức | compareTo(T o) | compare(T o1, T o2) |
| Sử Dụng | Sử dụng khi đối tượng tự nó có khả năng so sánh. | Sử dụng khi muốn so sánh đối tượng theo nhiều cách khác nhau hoặc khi không thể sửa đổi lớp đối tượng. |
| Thứ Tự Sắp Xếp Mặc Định | Định nghĩa thứ tự sắp xếp tự nhiên (natural order) cho đối tượng. | Định nghĩa thứ tự sắp xếp tùy chỉnh (custom order) ngoài thứ tự tự nhiên. |
| Triển Khai (Implementation) | Lớp cần sắp xếp implement giao diện Comparable. | Lớp so sánh riêng implement giao diện Comparator. |
| Thay Đổi Đối Tượng | Yêu cầu sửa đổi lớp đối tượng để implement Comparable. | Không yêu cầu sửa đổi lớp đối tượng, có thể tạo nhiều lớp Comparator khác nhau cho một lớp đối tượng. |
| Số Cách So Sánh | Chỉ có một cách so sánh. | Có thể có nhiều cách so sánh khác nhau. |
| Xử Lý null | Phải xử lý thủ công trong phương thức compareTo. | Có thể tùy chỉnh cách xử lý null dễ dàng. |
| Ví Dụ Sử Dụng | Collections.sort(List<T> list) khi T implements Comparable. | Collections.sort(List<T> list, Comparator<T> c) khi sử dụng Comparator. |
Khi nào nên sử dụng Comparable?
↳ Khi bạn muốn định nghĩa thứ tự sắp xếp tự nhiên cho một lớp (ví dụ: sắp xếp các đối tượng String theo thứ tự bảng chữ cái, sắp xếp các đối tượng Integer theo thứ tự số học).
↳ Khi thứ tự sắp xếp của đối tượng là cố định và được xác định trong chính lớp đó.
Khi nào nên sử dụng Comparator?
↳ Khi bạn muốn so sánh các đối tượng theo nhiều cách khác nhau (ví dụ: so sánh các đối tượng Person theo tên, tuổi, hoặc địa chỉ).
↳ Khi bạn không thể sửa đổi lớp đối tượng, nhưng vẫn muốn sắp xếp nó (ví dụ: lớp đối tượng thuộc thư viện bên ngoài hoặc lớp đối tượng đã có sẵn).
↳ Khi bạn cần một cách sắp xếp tùy chỉnh mà không làm thay đổi cách so sánh tự nhiên của đối tượng.
So Sánh Xử Lý null trong Interface Comparable và Interface Comparator
Trong Java, khi so sánh các đối tượng, bạn có thể gặp trường hợp một hoặc cả hai đối tượng là null. Việc xử lý null phải được thực hiện cẩn thận để tránh các lỗi như NullPointerException. Dưới đây là sự khác nhau giữa xử lý null trong Comparable và Comparator như sau:
| Tiêu chí | Comparable | Comparator |
|---|---|---|
| Xử lý null | Cần tự xử lý trong phương thức compareTo(). | Sử dụng sẵn các tiện ích như Comparator.nullsFirst() hoặc Comparator.nullsLast(). |
| Độ linh hoạt | Ít linh hoạt, phải tùy chỉnh trong từng lớp triển khai. | Linh hoạt hơn, có thể áp dụng các phương thức xử lý null mà không thay đổi đối tượng. |
Kết luận
↳ Với Comparable, bạn phải tự viết logic xử lý null trong phương thức compareTo().
↳ Với Comparator, việc xử lý null trở nên dễ dàng hơn và có thể được thực hiện bằng cách sử dụng các phương thức hỗ trợ như nullsFirst() và nullsLast().
Destructive và Non-Destructive
Trong lập trình, đặc biệt là khi làm việc với các cấu trúc dữ liệu như danh sách, mảng, hoặc các đối tượng khác, một số phương thức hoặc thao tác có thể thuộc loại destructive (phá hủy) hoặc non-destructive (không phá hủy) tùy thuộc vào cách chúng xử lý dữ liệu dựa trên dữ liệu gốc. Dưới đây là giải thích chi tiết:
Destructive (Phá hủy)
Ý nghĩa: Một hoạt động destructive là một hoạt động thay đổi vĩnh viễn dữ liệu gốc. Sau khi hoạt động này được thực hiện, dữ liệu ban đầu sẽ bị ghi đè hoặc xóa đi, không thể khôi phục lại trạng thái ban đầu trừ khi có bản sao lưu.
Ví dụ:
↳ Ghi đè file: Khi bạn ghi đè một file, dữ liệu cũ trong file đó sẽ bị thay thế hoàn toàn bởi dữ liệu mới.
↳ Xóa file: Hành động xóa một file sẽ di chuyển file đó vào thùng rác (trên các hệ điều hành có thùng rác) hoặc xóa vĩnh viễn, khiến dữ liệu không thể truy cập được.
↳ Sửa đổi trực tiếp một cấu trúc dữ liệu: Khi bạn thay đổi một phần tử trong một mảng hoặc danh sách, giá trị cũ của phần tử đó sẽ bị thay thế.
Cụ thể hơn:
↳ add(E element) trong List: Thay đổi danh sách gốc.
↳ remove(int index) trong List: Xóa phần tử trong danh sách gốc.
↳ clear() trong Map: Xóa tất cả các phần tử trong map
Non-destructive (Không phá hủy)
Ý nghĩa: Một hoạt động non-destructive là một hoạt động tạo ra một bản sao của dữ liệu gốc và thực hiện các thay đổi trên bản sao đó. Dữ liệu gốc vẫn được giữ nguyên và không bị ảnh hưởng.
Ví dụ:
↳ Sao chép file: Khi bạn sao chép một file, bạn sẽ tạo ra một bản sao hoàn toàn giống với file gốc. Bạn có thể thực hiện các thay đổi trên bản sao mà không ảnh hưởng đến file gốc.
↳ Tạo một bản sao của một cấu trúc dữ liệu: Bạn có thể tạo một bản sao của một mảng hoặc danh sách và thực hiện các thay đổi trên bản sao đó mà không ảnh hưởng đến bản gốc.
↳ Sử dụng các hàm biến đổi: Nhiều hàm trong lập trình (như hàm map, filter trong các ngôn ngữ lập trình hàm) tạo ra một cấu trúc dữ liệu mới dựa trên một cấu trúc dữ liệu hiện có mà không thay đổi cấu trúc gốc.
Cụ thể hơn:
↳ toUpperCase() trong String: Tạo ra một chuỗi mới.
↳ subList(int fromIndex, int toIndex) trong List: Tạo ra một danh sách con.
↳ replace(CharSequence target, CharSequence replacement) trong String: Trả về một chuỗi mới với các ký tự đã được thay thế.
↳ String.substring(int beginIndex): Trả về một chuỗi con của chuỗi gốc, không thay đổi chuỗi gốc.
↳ Arrays.copyOf(int[] original, int newLength): Tạo một mảng mới có cùng các phần tử với mảng gốc hoặc một phần của nó.
Tóm tắt
| Thuật ngữ | Ý nghĩa | Ví dụ |
|---|---|---|
| Destructive | Thay đổi vĩnh viễn dữ liệu gốc | Ghi đè file, xóa file, sửa đổi trực tiếp cấu trúc dữ liệu |
| Non-destructive | Tạo bản sao và thay đổi bản sao | Sao chép file, tạo bản sao cấu trúc dữ liệu, sử dụng hàm biến đổi |
↳ Destructive: Là hành động thay đổi dữ liệu ban đầu, không thể khôi phục lại trạng thái ban đầu một cách dễ dàng.
↳ Non-destructive: Là hành động không thay đổi dữ liệu ban đầu, dữ liệu gốc vẫn còn nguyên vẹn sau khi thao tác.
Trong lập trình, việc hiểu rõ sự khác biệt giữa destructive và non-destructive rất quan trọng để tránh các lỗi không mong muốn và bảo vệ dữ liệu. Khi làm việc với các cấu trúc dữ liệu lớn, việc sử dụng các hoạt động non-destructive có thể giúp tiết kiệm tài nguyên và tăng tính bảo mật.
Như vậy, chúng ta đã tìm hiểu về giao diện Comparator<T>. Khác với Comparable chỉ cung cấp một thứ tự sắp xếp duy nhất, Comparator cho phép bạn tạo ra nhiều cách sắp xếp tùy chỉnh cho cùng một lớp. Đây là một công cụ cực kỳ mạnh mẽ và linh hoạt, giúp bạn kiểm soát hoàn toàn thứ tự của các phần tử trong các cấu trúc dữ liệu có sắp xếp như TreeSet hoặc TreeMap, từ đó đáp ứng mọi yêu cầu sắp xếp phức tạp của ứng dụng.