Cách Sắp Xếp Danh Sách trong Java: list.sort, Comparator, Nhiều Điều Kiện và Xử Lý Null

目次

1. Những Điều Bạn Sẽ Học Trong Bài Viết Này (Cách Nhanh Nhất Để Sắp Xếp Một Java List)

Khi làm việc với Java, rất thường xuyên bạn sẽ gặp phải các tình huống cần sắp xếp một List.
Cùng lúc đó, nhiều lập trình viên—đặc biệt là người mới—thường bối rối với những câu hỏi như:

  • Tôi nên dùng phương thức nào để sắp xếp một List?
  • Sự khác nhau giữa list.sort()Collections.sort() là gì?
  • Làm sao để sắp xếp một List các đối tượng thay vì các giá trị đơn giản?

Bài viết này được thiết kế để trả lời những câu hỏi một cách rõ ràng và thực tiễn, bắt đầu bằng kết luận, sau đó dần dần giải thích chi tiết và các trường hợp sử dụng thực tế.

1.1 Kết Luận: Đây Là Mẫu Đơn Giản Nhất Bạn Cần Nhớ

Nếu bạn chỉ muốn cách ngắn gọn và chuẩn nhất để sắp xếp một List trong Java, thì đây là nó:

list.sort(Comparator.naturalOrder());

Điều này sẽ sắp xếp List theo thứ tự tăng dần (nhỏ nhất tới lớn nhất, A tới Z, cũ nhất tới mới nhất).

Nếu bạn muốn theo thứ tự giảm dần, hãy dùng đoạn này thay thế:

list.sort(Comparator.reverseOrder());

Chỉ với hai dòng này, bạn đã có thể sắp xếp các List của:

  • Integer
  • String
  • LocalDate
  • và hầu hết các kiểu dữ liệu phổ biến khác

Đối với nhiều trường hợp hàng ngày, đây là tất cả những gì bạn cần.

1.2 Sắp Xếp List Các Đối Tượng: Xác Định Khóa Sắp Xếp

Trong các ứng dụng thực tế, List thường chứa các đối tượng, không phải các giá trị đơn giản.

Ví dụ:

class Person {
    private String name;
    private int age;

    // getters omitted
}

Để sắp xếp một List<Person> theo tuổi, viết như sau:

list.sort(Comparator.comparing(Person::getAge));

Dòng duy nhất này có nghĩa là:

  • Lấy age từ mỗi Person
  • So sánh các giá trị đó
  • Sắp xếp List theo thứ tự tăng dần

Bạn không cần tự viết logic so sánh.
Chỉ cần nhớ mẫu này:

Comparator.comparing(whatToCompare)

1.3 Nội Dung Bài Viết

Bài viết giải thích cách sắp xếp Java List từ cơ bản đến thực tiễn, bao gồm:

  • Sự khác nhau giữa list.sort()Collections.sort()
  • Cách hoạt động của Comparator một cách đơn giản
  • Sắp xếp với nhiều điều kiện (ví dụ: tuổi → tên)
  • Kết hợp thứ tự tăng và giảm
  • Xử lý an toàn các giá trị null
  • Khi nào nên dùng stream().sorted() thay vì list.sort()

Mục tiêu không phải ghi nhớ, mà là hiểu tại sao mỗi cách lại tồn tại.

1.4 Đối Tượng Đọc Bài Viết

Bài viết này dành cho các lập trình viên mà:

  • Hiểu cú pháp cơ bản của Java (lớp, phương thức, List)
  • Đã từng dùng ArrayList hoặc List
  • Vẫn phải tra cứu mã sắp xếp mỗi khi cần

không hướng tới những người hoàn toàn mới chưa từng viết mã Java,
nhưng thân thiện với người mới bắt đầu muốn học lập trình Java thực tế.

2. Ba Cách Thông Dụng Để Sắp Xếp Một List Trong Java (Bạn Nên Dùng Cách Nào?)

Trong Java, không chỉ có một cách duy nhất để sắp xếp một List.
Trong thực tế, các lập trình viên chủ yếu dùng ba phương pháp khác nhau, mỗi phương pháp có hành vi và mục đích hơi khác nhau.

Trước khi đi sâu vào Comparator, quan trọng là phải hiểu khi nào và tại sao mỗi tùy chọn tồn tại.

2.1 list.sort(Comparator) — Cách Hiện Đại và Được Đề Xuất

Kể từ Java 8, đây là cách chuẩn và được khuyến nghị nhất để sắp xếp một List.

list.sort(Comparator.naturalOrder());

Đặc điểm chính

  • Được định nghĩa trực tiếp trên giao diện List
  • Rõ ràng và dễ đọc
  • Sắp xếp List gốc tại chỗ (destructive)

Sắp xếp các đối tượng cũng hoạt động tương tự:

list.sort(Comparator.comparing(Person::getAge));

Khi nào nên dùng

  • Khi bạn đồng ý việc List gốc sẽ bị thay đổi
  • Khi bạn muốn giải pháp rõ ràng và đơn giản nhất

👉 Nếu bạn không chắc, list.sort() thường là lựa chọn đúng.

2.2 Collections.sort(list) — Kiểu Cũ Bạn Vẫn Cần Nhận Biết

Bạn có thể thấy mã như sau trong các tutorial cũ hoặc dự án legacy:

Collections.sort(list);

Hoặc với một Comparator:

Collections.sort(list, Comparator.reverseOrder());

Đặc điểm chính

  • Có từ Java 1.2
  • Nội bộ hoạt động gần như tương tự list.sort
  • Cũng thay đổi List gốc

Tại sao nó ít được dùng hiện nay

  • Java 8 đã giới thiệu list.sort, cảm giác tự nhiên hơn
  • Sắp xếp List bằng Collections ít trực quan hơn

Đối với mã mới, list.sort được ưu tiên.
Tuy nhiên, việc hiểu Collections.sort vẫn quan trọng khi đọc các codebase cũ.

2.3 stream().sorted() — Sắp xếp không phá hủy

Tùy chọn thứ ba sử dụng Stream API.

List<Integer> sorted =
    list.stream()
        .sorted()
        .toList();

Với một Comparator:

List<Person> sorted =
    list.stream()
        .sorted(Comparator.comparing(Person::getAge))
        .toList();

Đặc điểm chính

  • KHÔNG thay đổi List gốc
  • Trả về một List mới đã được sắp xếp
  • Dễ dàng kết hợp với filter, map, và các thao tác stream khác

Khi nào nên dùng

  • Khi bạn phải giữ List gốc không thay đổi
  • Khi sắp xếp là một phần của pipeline xử lý dữ liệu

Đối với sắp xếp đơn giản, list.sort thường rõ ràng và hiệu quả hơn.

2.4 Cách chọn (Hướng dẫn quyết định nhanh)

GoalRecommended Method
Sort a List directlylist.sort()
Understand or maintain old codeCollections.sort()
Keep the original List unchangedstream().sorted()

Ở thời điểm này, hãy nhớ một quy tắc duy nhất:

Sử dụng list.sort() trừ khi bạn có lý do rõ ràng để không dùng.

3. Sắp xếp cơ bản: Thứ tự tăng và giảm

Bây giờ bạn đã biết phương pháp sắp xếp nào cần dùng, hãy tập trung vào yêu cầu phổ biến nhất:
sắp xếp tăng hoặc giảm.

Phần này đề cập đến các List của các kiểu dữ liệu cơ bản như số, chuỗi và ngày—nền tảng cho mọi thứ tiếp theo.

3.1 Sắp xếp tăng dần (Thứ tự tự nhiên)

Nhiều kiểu Java có thứ tự tự nhiên, chẳng hạn như:

  • Số → từ nhỏ tới lớn
  • Chuỗi → thứ tự bảng chữ cái (A đến Z)
  • Ngày → từ cũ tới mới

Để sắp xếp một List tăng dần, dùng:

list.sort(Comparator.naturalOrder());

Ví dụ: Sắp xếp số

List<Integer> numbers = Arrays.asList(5, 1, 3, 2);
numbers.sort(Comparator.naturalOrder());

Kết quả:

[1, 2, 3, 5]

Ví dụ: Sắp xếp chuỗi

List<String> names = Arrays.asList("Tom", "Alice", "Bob");
names.sort(Comparator.naturalOrder());

Kết quả:

[Alice, Bob, Tom]

👉 Nếu bạn chỉ muốn thứ tự tăng, đây là cách đơn giản và an toàn nhất.

3.2 Sắp xếp giảm dần

Để đảo ngược thứ tự tự nhiên, dùng Comparator.reverseOrder().

list.sort(Comparator.reverseOrder());

Ví dụ: Số giảm dần

List<Integer> numbers = Arrays.asList(5, 1, 3, 2);
numbers.sort(Comparator.reverseOrder());

Kết quả:

[5, 3, 2, 1]

Ví dụ: Chuỗi giảm dần

List<String> names = Arrays.asList("Tom", "Alice", "Bob");
names.sort(Comparator.reverseOrder());

Kết quả:

[Tom, Bob, Alice]

3.3 Khi có thể bỏ qua Comparator

Trong một số trường hợp, bạn có thể thấy mã sắp xếp được viết mà không có Comparator rõ ràng.

Collections.sort(list);

Hoặc thậm chí:

list.sort(null);

Các cách này chỉ hoạt động nếu:

  • Các phần tử triển khai Comparable
  • Bạn muốn thứ tự tự nhiên (tăng dần)

Mặc dù hợp lệ, các kiểu này ít rõ ràng hơn.
Trong các codebase thực tế, phiên bản này thường được ưu tiên vì tính rõ ràng:

list.sort(Comparator.naturalOrder());

3.4 Hiểu lầm phổ biến: Ai quyết định thứ tự?

Một nguồn gây nhầm lẫn thường gặp cho người mới là nghĩ rằng:

sort() quyết định thứ tự tăng hay giảm

Thực tế:

  • sort() thực hiện việc sắp xếp
  • Comparator xác định cách các phần tử được so sánh

Khi bạn hiểu được sự tách biệt này,
mọi thứ khác—sắp xếp đối tượng, nhiều điều kiện, và xử lý null—sẽ trở nên dễ dàng hơn rất nhiều.

4. Sắp xếp List các đối tượng: Hiểu về Comparator

Trong các ứng dụng Java thực tế, bạn hiếm khi sắp xếp các giá trị đơn giản như Integer hay String.
Hầu hết thời gian, bạn sẽ sắp xếp Danh sách các đối tượng tùy chỉnh.

Đây là nơi Comparator trở thành khái niệm cốt lõi.

4.1 Comparator là gì?

Một Comparator định nghĩa cách hai phần tử nên được so sánh.

Về mặt khái niệm, nó trả lời câu hỏi:

“Cho hai đối tượng, đối tượng nào nên đứng trước?”

Bên trong, một Comparator trả về:

  • Một số âm → phần tử đầu tiên đứng trước
  • Số 0 → thứ tự không quan trọng
  • Một số dương → phần tử thứ hai đứng trước

May mắn là trong Java hiện đại bạn hầu như không cần tự viết logic này bằng tay.

4.2 Sắp xếp theo một trường bằng Comparator.comparing

Xem xét lớp sau:

class Person {
    private String name;
    private int age;

    // getters omitted
}

Để sắp xếp một List<Person> theo tuổi, dùng:

list.sort(Comparator.comparing(Person::getAge));

Cách này đọc một cách tự nhiên:

  • Lấy từng Person
  • Trích xuất age
  • So sánh các giá trị đó
  • Sắp xếp List theo thứ tự tăng dần

Một dòng duy nhất này thay thế nhiều dòng code so sánh cũ.

4.3 Sắp xếp theo String, Date và các kiểu khác

Mẫu tương tự hoạt động cho hầu hết mọi kiểu trường.

Sắp xếp theo tên

list.sort(Comparator.comparing(Person::getName));

Sắp xếp theo ngày

list.sort(Comparator.comparing(Person::getBirthDate));

Miễn là giá trị được trích xuất có thứ tự tự nhiên,
Comparator.comparing sẽ hoạt động mà không cần cấu hình thêm.

4.4 Sử dụng Comparator chuyên cho các kiểu nguyên thủy

Đối với các trường số, Java cung cấp các phương thức tối ưu:

  • comparingInt
  • comparingLong
  • comparingDouble

Ví dụ:

list.sort(Comparator.comparingInt(Person::getAge));

Các phương thức này:

  • Tránh việc đóng gói đối tượng không cần thiết
  • Làm cho ý định của bạn rõ ràng hơn
  • Hơi hiệu quả hơn đối với các List lớn

Mặc dù sự khác biệt là nhỏ, chúng được coi là thực hành tốt cho các trường số.

4.5 Tại sao điều này quan trọng

Khi bạn hiểu Comparator.comparing, bạn sẽ mở khóa:

  • Sắp xếp đa điều kiện
  • Kết hợp thứ tự tăng và giảm
  • Xử lý an toàn các giá trị null

Nói cách khác, đây là nền tảng của việc sắp xếp List thực tế trong Java.

5. Sắp xếp với nhiều điều kiện (Mẫu thực tế phổ biến nhất)

Trong các ứng dụng thực tế, sắp xếp chỉ theo một trường thường không đủ.
Bạn thường cần các điều kiện phụ và thứ ba để tạo ra một thứ tự ổn định và có ý nghĩa.

Các ví dụ bao gồm:

  • Sắp xếp theo tuổi, rồi theo tên
  • Sắp xếp theo mức ưu tiên, rồi theo thời gian
  • Sắp xếp theo điểm (giảm dần), rồi theo ID (tăng dần)

API Comparator của Java được thiết kế riêng cho mục đích này.

5.1 Cơ bản về thenComparing

Sắp xếp đa điều kiện tuân theo một quy tắc đơn giản:

Nếu hai phần tử bằng nhau theo điều kiện đầu tiên, dùng điều kiện tiếp theo.

Đây là mẫu cơ bản:

list.sort(
    Comparator.comparingInt(Person::getAge)
              .thenComparing(Person::getName)
);

Nghĩa là:

  1. Sắp xếp theo age (tăng dần)
  2. Nếu tuổi bằng nhau, sắp xếp theo name (tăng dần)

Kết quả là một thứ tự nhất quán và dự đoán được.

5.2 Kết hợp thứ tự tăng và giảm

Rất thường xuyên, bạn muốn một trường sắp xếp giảm dần và trường khác tăng dần.

Ví dụ: Điểm (giảm dần), Tên (tăng dần)

list.sort(
    Comparator.comparingInt(Person::getScore).reversed()
              .thenComparing(Person::getName)
);

Chi tiết quan trọng:

  • reversed() chỉ áp dụng cho Comparator ngay trước nó

Điều này giúp bạn kết hợp an toàn các hướng sắp xếp khác nhau.

5.3 Truyền Comparator vào thenComparing

Để mã dễ đọc hơn, bạn có thể xác định rõ hướng sắp xếp bên trong thenComparing.

Ví dụ: Tuổi (tăng dần), Ngày đăng ký (giảm dần)

list.sort(
    Comparator.comparingInt(Person::getAge)
              .thenComparing(
                  Comparator.comparing(Person::getRegisterDate).reversed()
              )
);

Kiểu này làm cho việc xác định trường nào tăng dần hay giảm dần trở nên rất rõ ràng, điều này hữu ích cho việc xem xét mã và bảo trì lâu dài.

5.4 Ví dụ Kinh doanh Thực tế

list.sort(
    Comparator.comparingInt(Order::getPriority)
              .thenComparing(Order::getDeadline)
              .thenComparing(Order::getOrderId)
);

Logic sắp xếp:

  1. Ưu tiên cao hơn trước
  2. Hạn chót sớm hơn trước
  3. ID đơn hàng thấp hơn trước

Điều này đảm bảo một thứ tự ổn định và thân thiện với kinh doanh.

5.5 Giữ cho Việc Sắp xếp Nhiều Điều kiện Dễ đọc

Khi logic sắp xếp phát triển, khả năng đọc hiểu trở nên quan trọng hơn so với độ ngắn gọn.

Các thực hành tốt nhất:

  • Tách dòng cho mỗi điều kiện
  • Tránh các lambda lồng nhau sâu
  • Thêm chú thích nếu các quy tắc kinh doanh không rõ ràng

Logic sắp xếp rõ ràng tiết kiệm thời gian cho mọi người đọc mã sau này.

6. Xử lý Giá trị null An toàn (Một Nguồn Lỗi Rất Thường Gặp)

Khi sắp xếp dữ liệu thực tế, giá trị null hầu như không thể tránh được. Các trường có thể là tùy chọn, dữ liệu cũ có thể không đầy đủ, hoặc giá trị có thể đơn giản là thiếu.

Nếu bạn không xử lý null một cách rõ ràng, việc sắp xếp có thể dễ dàng thất bại khi chạy.

6.1 Tại sao null Gây Vấn đề Khi Sắp xếp

Xem xét đoạn mã này:

list.sort(Comparator.comparing(Person::getName));

Nếu getName() trả về null cho bất kỳ phần tử nào, Java sẽ ném ra NullPointerException trong quá trình so sánh.

Điều này xảy ra vì:

  • Một Comparator giả định các giá trị có thể so sánh được
  • null không có thứ tự tự nhiên trừ khi bạn định nghĩa một thứ tự

Do đó, việc xử lý null phải được thực hiện một cách rõ ràng.

6.2 Sử dụng nullsFirstnullsLast

Java cung cấp các phương thức trợ giúp để định nghĩa cách sắp xếp các giá trị null.

Đặt giá trị null lên đầu

list.sort(
    Comparator.comparing(
        Person::getName,
        Comparator.nullsFirst(Comparator.naturalOrder())
    )
);

Đặt giá trị null xuống cuối

list.sort(
    Comparator.comparing(
        Person::getName,
        Comparator.nullsLast(Comparator.naturalOrder())
    )
);

Các cách tiếp cận này:

  • Ngăn ngừa NullPointerException
  • Làm cho quy tắc sắp xếp rõ ràng và dễ đọc

6.3 Khi Danh sách Bản thân Chứa Các Phần tử null

Đôi khi, các phần tử của Danh sách có thể là null.

List<Person> list = Arrays.asList(
    new Person("Alice", 20),
    null,
    new Person("Bob", 25)
);

Để xử lý điều này một cách an toàn:

list.sort(
    Comparator.nullsLast(
        Comparator.comparing(Person::getName)
    )
);

Điều này đảm bảo:

  • Các phần tử null được chuyển đến cuối danh sách
  • Các phần tử không phải null được sắp xếp bình thường

6.4 Cẩn thận với comparingIntnull

Các comparator đặc thù cho kiểu nguyên thủy như comparingInt không thể xử lý null.

Comparator.comparingInt(Person::getAge); // age must be int

Nếu trường là một Integer có thể là null, hãy sử dụng:

Comparator.comparing(
    Person::getAge,
    Comparator.nullsLast(Integer::compare)
);

Điều này tránh các lỗi thời gian chạy không mong muốn.

6.5 Xem việc Xử lý null như một Phần của Đặc tả

Quyết định liệu các giá trị null có nên xuất hiện:

  • Ở đầu
  • Ở cuối
  • Hoặc được lọc bỏ hoàn toàn

đó là một quyết định kinh doanh, không chỉ là một quyết định kỹ thuật.

Bằng cách sử dụng nullsFirst hoặc nullsLast, bạn ghi lại quyết định đó trực tiếp trong mã— làm cho logic sắp xếp của bạn an toàn hơn và dễ hiểu hơn.

7. Các Cạm Bẫy và Sai Lầm Thông Thường (Cách Tránh Các Lỗi Nhỏ Nhặt)

Việc sắp xếp một List trong Java trông đơn giản, nhưng có một số cạm bẫy dễ bỏ qua có thể dẫn đến lỗi, hành vi không mong đợi hoặc vấn đề hiệu năng.

Hiểu trước những điều này sẽ tiết kiệm thời gian của bạn trong quá trình gỡ lỗi và xem xét mã.

7.1 Quên Rằng Việc Sắp xếp Là Hành Động Phá Hủy

Cả list.sort()Collections.sort() sửa đổi List gốc.

List<Integer> original = new ArrayList<>(List.of(3, 1, 2));
List<Integer> alias = original;

original.sort(Comparator.naturalOrder());

Trong trường hợp này:

  • original được sắp xếp
  • alias cũng được sắp xếp (vì chúng tham chiếu cùng một List)

Cách tránh điều này

Nếu bạn cần giữ nguyên thứ tự gốc:

List<Integer> sorted = new ArrayList<>(original);
sorted.sort(Comparator.naturalOrder());

Hoặc sử dụng streams:

List<Integer> sorted =
    original.stream()
            .sorted()
            .toList();

Luôn tự hỏi mình:
“Có nên sửa đổi List gốc không?”

7.2 Tính nhất quán của Comparator và thứ tự ổn định

Một Comparator nên tạo ra kết quả nhất quán và dự đoán được.

Ví dụ:

Comparator.comparing(Person::getAge);

Nếu nhiều người có cùng tuổi, thứ tự tương đối của chúng không được xác định.

Điều này có thể chấp nhận được—nhưng thường không.

Thực hành tốt nhất

Thêm một điều kiện phụ để ổn định thứ tự:

Comparator.comparingInt(Person::getAge)
          .thenComparing(Person::getId);

Điều này đảm bảo kết quả sắp xếp là quyết định được.

7.3 Phân biệt chữ hoa/chữ thường trong sắp xếp String

Thứ tự tự nhiên của Stringphân biệt chữ hoa/chữ thường.

List<String> list = List.of("apple", "Banana", "orange");
list.sort(Comparator.naturalOrder());

Điều này có thể tạo ra kết quả không trực quan.

Sắp xếp không phân biệt chữ hoa/chữ thường

list.sort(String.CASE_INSENSITIVE_ORDER);

Trước khi chọn, hãy cân nhắc:

  • Đây có phải để hiển thị không?
  • Hoặc cho logic nội bộ?

Câu trả lời sẽ quyết định cách tiếp cận đúng.

7.4 Thực hiện công việc nặng trong Comparator

Một Comparator có thể được gọi nhiều lần trong quá trình sắp xếp.

Tránh:

  • Truy cập cơ sở dữ liệu
  • Gọi mạng
  • Các phép tính tốn kém
    // Bad idea (conceptual example)
    Comparator.comparing(p -> expensiveOperation(p));
    

Cách tiếp cận tốt hơn

  • Tính trước các giá trị
  • Lưu chúng trong các trường
  • So sánh các giá trị đơn giản, rẻ tiền

Comparator hiệu quả tạo ra sự khác biệt lớn với các List lớn.

7.5 Ưu tiên tính dễ đọc hơn sự khéo léo

Logic sắp xếp thường được đọc nhiều hơn số lần nó được viết.

Thay vì:

  • Một biểu thức chuỗi dài
  • Các lambda lồng nhau sâu

Ưu tiên:

  • Ngắt dòng
  • Cấu trúc rõ ràng
  • Các chú thích tùy chọn cho quy tắc nghiệp vụ

Mã sắp xếp dễ đọc giảm lỗi và làm cho việc bảo trì dễ dàng hơn.

8. Các cân nhắc về hiệu năng và chọn cách tiếp cận đúng

Bây giờ, bạn đã biết cách sắp xếp List trong Java.
Phần này tập trung vào cách nào nên chọn từ góc độ hiệu năng và thiết kế.

Trong hầu hết các ứng dụng, sắp xếp không phải là nút thắt—nhưng các lựa chọn sai vẫn có thể gây ra chi phí không cần thiết.

8.1 list.sort() vs stream().sorted()

Đây là điểm quyết định phổ biến nhất.

list.sort()

list.sort(Comparator.comparingInt(Person::getAge));

Ưu điểm

  • Không cần cấp phát List mới
  • Ý định rõ ràng: “sắp xếp List này”
  • Hiệu quả hơn một chút

Nhược điểm

  • Sửa đổi List gốc

stream().sorted()

List<Person> sorted =
    list.stream()
        .sorted(Comparator.comparingInt(Person::getAge))
        .toList();

Ưu điểm

  • List gốc vẫn không thay đổi
  • Phù hợp tự nhiên vào các pipeline stream

Nhược điểm

  • Cấp phát một List mới
  • Một chút chi phí hơn

Quy tắc thực tế

  • Sắp xếp đơn giảnlist.sort()
  • Pipeline chuyển đổi hoặc tính bất biếnstream().sorted()

8.2 Sắp xếp List lớn một cách hiệu quả

Thuật toán sắp xếp gọi Comparator nhiều lần. Đối với List lớn, điều này quan trọng.

Hướng dẫn chính

  • Giữ comparator nhẹ
  • Tránh chuỗi phương thức thực hiện công việc nặng
  • Ưu tiên comparator nguyên thủy (comparingInt, v.v.)

Ví dụ: Tính trước các khóa tốn kém

Thay vì:

Comparator.comparing(p -> calculateScore(p));

Thực hiện:

// Precompute once
p.setScore(calculateScore(p));

Sau đó sắp xếp theo trường:

Comparator.comparingInt(Person::getScore);

Điều này giảm đáng kể công việc lặp lại trong quá trình sắp xếp.

8.3 Collections.sort() Có Bao Giờ Là Lựa Chọn Đúng?

Đối với mã mới, hầu như không bao giờ.

Tuy nhiên, nó vẫn xuất hiện trong:

  • Các dự án kế thừa
  • Các hướng dẫn cũ
  • Các codebase Java 7 và trước đó

Bạn không cần phải sử dụng nó—nhưng bạn nên nhận ra nó.

8.4 Recommended Decision Checklist

Trước khi sắp xếp, hãy hỏi:

  1. Tôi có thể sửa đổi List gốc không?
  2. Tôi có cần nhiều điều kiện sắp xếp không?
  3. Có trường nào có thể là null không?
  4. Hiệu năng có quan trọng khi quy mô lớn không?

Trả lời những câu hỏi này sẽ tự nhiên dẫn đến giải pháp đúng.

9. Summary: Java List Sort Cheat Sheet

Hãy tổng hợp mọi thứ thành một hướng dẫn tham khảo nhanh mà bạn có thể tin cậy.

9.1 Quick Patterns You’ll Use Most Often

Ascending order

list.sort(Comparator.naturalOrder());

Descending order

list.sort(Comparator.reverseOrder());

9.2 Sorting Objects by a Field

list.sort(Comparator.comparing(Person::getName));

For numbers:

list.sort(Comparator.comparingInt(Person::getAge));

9.3 Multiple Conditions

list.sort(
    Comparator.comparingInt(Person::getAge)
              .thenComparing(Person::getName)
);

Mixed order:

list.sort(
    Comparator.comparingInt(Person::getScore).reversed()
              .thenComparing(Person::getName)
);

9.4 Safe null Handling

list.sort(
    Comparator.comparing(
        Person::getName,
        Comparator.nullsLast(Comparator.naturalOrder())
    )
);

List may contain null elements:

list.sort(
    Comparator.nullsLast(
        Comparator.comparing(Person::getName)
    )
);

9.5 Non-Destructive Sorting

List<Person> sorted =
    list.stream()
        .sorted(Comparator.comparingInt(Person::getAge))
        .toList();

9.6 Final Takeaway

Java List sorting becomes simple once you remember:

  • Comparator xác định thứ tự
  • sort() thực hiện thao tác
  • Rõ ràng thắng hơn sự khéo léo
  • Xử lý null một cách rõ ràng ngăn ngừa lỗi

Nếu bạn nắm vững những nguyên tắc này,
Bạn sẽ không bao giờ cần “học lại” việc sắp xếp List trong Java nữa.