VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Cấu trúc dữ liệu và giải thuậtTìm kiếm và sắp xếp
Bài 12/18
5 phút

Sắp xếp trong Java

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Hai bài trước tự viết thuật toán sắp xếp để hiểu bên trong. Khi đi làm, bạn gần như luôn dùng method có sẵn của Java vì nhanh, đã được kiểm thử kỹ và gọn. Điều cần biết là nên chọn method nào, và method nào đổi luôn list gốc.

Khái niệm

⚖️ Sắp xếp ổn định (stable sort): các phần tử bằng nhau theo tiêu chí sắp xếp vẫn giữ đúng thứ tự ban đầu của chúng.

Cách Đổi list gốc? Ổn định?
list.sort(...), Collections.sort(...) có, sắp xếp tại chỗ có
stream().sorted(...) không, trả về list mới có
Arrays.sort(...) trên array int, long có, sắp xếp tại chỗ không cần: số bằng nhau không phân biệt được

Tất cả đều O(n log n). sorted đã gặp ở bài Stream cơ bản của khoá Java Core.

Ví dụ

// Main.java
void main() {
    List<Product> products = new ArrayList<>(List.of(
        new Product("Vở", 12000),
        new Product("Bút bi", 5000),
        new Product("Thước", 7000)));

    products.sort((a, b) ->
        Long.compare(a.getPrice(), b.getPrice()));
    for (Product p : products) {
        System.out.println("%s: %d".formatted(
            p.getName(), p.getPrice()));
    }
}

// Product.java
class Product {
    private String name;
    private long price;

    public Product(String name, long price) {
        this.name = name;
        this.price = price;
    }

    public String getName() { return name; }
    public long getPrice() { return price; }
}
  • sort nhận lambda so hai phần tử a, b. Kết quả âm nghĩa là a đứng trước, dương là b đứng trước, 0 là bằng nhau.
  • Long.compare(a.getPrice(), b.getPrice()) trả về đúng số âm, 0 hoặc dương như vậy.
  • Muốn giảm dần thì đổi chỗ: Long.compare(b.getPrice(), a.getPrice()).
  • sort đổi thứ tự ngay trong products, in ra Bút bi, Thước, Vở. List tạo bằng List.of không sửa được, nên được bọc trong new ArrayList<>(...).

Thử ngay

Hai món cùng giá 5000. Giữ Product.java, thay Main.java bằng đoạn dưới và xem món nào đứng trước:

// Main.java
void main() {
    List<Product> items = new ArrayList<>(List.of(
        new Product("Vở", 12000),
        new Product("Bút chì", 5000),
        new Product("Thước", 7000),
        new Product("Bút bi", 5000)));

    items.sort(Comparator.comparingLong(
        p -> p.getPrice()));
    for (Product p : items) {
        System.out.println("%s: %d".formatted(
            p.getName(), p.getPrice()));
    }
}

Đoán trước khi chạy: "Bút chì" và "Bút bi" cùng giá. Món nào in ra trước?

Xem kết quả
Bút chì: 5000
Bút bi: 5000
Thước: 7000
Vở: 12000

sort của list ổn định: hai món cùng giá giữ thứ tự trong list gốc, nên "Bút chì" đứng trước "Bút bi". Muốn các món cùng giá xếp theo tên thì nối thêm .thenComparing(...) vào Comparator.

Lỗi hay gặp

Gọi stream().sorted(...) rồi tưởng list đã đổi. sorted trả về kết quả mới, list gốc giữ nguyên. Không dùng kết quả trả về thì coi như chưa sắp xếp.

// SAI — items vẫn giữ thứ tự lúc tạo
items.stream()
    .sorted(Comparator.comparingLong(p -> p.getPrice()))
    .toList();
System.out.println(items.get(0).getName());   // Vở
// ĐÚNG — dùng list mà toList() trả về
List<Product> sorted = items.stream()
    .sorted(Comparator.comparingLong(p -> p.getPrice()))
    .toList();
System.out.println(sorted.get(0).getName());
// Bút chì

Tóm tắt

  • list.sort và Collections.sort sắp xếp tại chỗ, O(n log n), ổn định.
  • stream().sorted(...) trả về kết quả mới, cũng ổn định.
  • Lambda so sánh trả số âm, 0 hoặc dương. Long.compare và Comparator.comparingLong làm sẵn việc đó.
  • list.sort của Java là TimSort: kết hợp merge sort với sắp xếp chèn cho đoạn ngắn.

Tự kiểm tra

0/3 câu
Câu 1

Cần sắp xếp đơn hàng theo ngày, đơn cùng ngày giữ nguyên thứ tự nhận được. Nên dùng gì?

Câu 2

list.sort((a, b) -> Long.compare(b.getPrice(), a.getPrice())) sắp xếp thế nào?

Câu 3

Đoạn code sau in ra gì?

products.stream()
    .sorted(Comparator.comparing(p -> p.getName()))
    .toList();
System.out.println(products.get(0));
Merge sortCây nhị phân tìm kiếm

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt