[Java] Tận dụng Lambda Expression trong bài toán sắp xếp

Cập nhật ngày: 29/03/2024 - Đã có 2114 lượt xem bài viết này!
[Java] Tận dụng Lambda Expression trong bài toán sắp xếp
Trong bài viết này chúng ta sẽ tận dụng một tính năng mới của Java 8 - Lambda Expression làm đòn bẩy phục vụ cho bài toán sắp xếp các phần tử thuộc Collection bất kỳ.

[Java] Tận dụng Lambda Expression trong bài toán sắp xếp

Trong bài viết này chúng ta sẽ tận dụng một tính năng mới của Java 8 - Lambda Expression làm đòn bẩy phục vụ cho bài toán sắp xếp các phần tử thuộc Collection bất kỳ.

Hãy bắt đầu với việc define class mô tả một Entity cụ thể:
 

public class Human {
    private String name;
    private int age;
 
    public Human() {
        super();
    }
 
    public Human(String name, int age) {
        super();
 
        this.name = name;
        this.age = age;
    }
 
// các bạn hãy tự thêm setter, getter, hashCode() như mặc định
}

 

1 Sắp xếp kiểu thông thường, không sử dụng Lambda Expression

Trước khi Lambda Expression chính thức có mặt trong Java 8, để sắp xếp một collection, người ta sẽ tạo một anonymous inner class cho Comparator như sau:
 

new Comparator() {
    @Override
    public int compare(Human h1, Human h2) {
        return h1.getName().compareTo(h2.getName());
    }
}


Và khi áp dụng nó để sắp xếp một Collection chứa các object kiểu Human (đã được define thông qua entity class ở trên), ta có:
 

@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    Collections.sort(humans, new Comparator() {
        @Override
        public int compare(Human h1, Human h2) {
            return h1.getName().compareTo(h2.getName());
        }
    });
//phần này phục vụ test
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}


2 Sắp xếp với Lambda Expression(mức cơ bản)

Ta sẽ tiết kiệm được khá nhiều diện tích và thời gian code khi áp dụng Lambda vào bài toán trên. Cụ thể, cú pháp tạo anonymous inner class sẽ được thay thế bằng:

(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());

Và khi áp dụng, ta có:

@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    humans.sort(
      (Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));

  // phần này phục vụ test
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}


Ở trường hợp này ta cũng áp dụng method sort() mới được bổ sung vào java.util.List trong bản Java 8 thay vì sử dụng Collections.sort() như truyền thống.

3 Sắp xếp mà không cần dùng Type Definition

Ta có thể thu gọn cú pháp sắp xếp một lần nữa bằng cách bỏ qua type definition, Compiler sẽ làm việc đó.

Thay vì:

(final Human h1, final Human h2) -> h1.getName().compareTo(h2.getName());

Thì ta sẽ gõ:

(h1, h2) -> h1.getName().compareTo(h2.getName())

Và áp dụng:
 

@Test
public void
  givenLambdaShortForm_whenSortingEntitiesByName_thenCorrectlySorted() {
     
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    humans.sort((h1, h2) -> h1.getName().compareTo(h2.getName()));
  
//phần này phục vụ test
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

4 Sắp xếp bằng cách sử dụng tham chiếu tới static method

Cách này vẫn xoay quanh lambda expression.

Đầu tiên, chúng ta vẫn define ra một method mới là compareByNameThenAge(). Phần method signature của nó cũng tương tự như method compare() thuộc Comparator:
 

public static int compareByNameThenAge(Human lhs, Human rhs) {
    if (lhs.name.equals(rhs.name)) {
        return lhs.age - rhs.age;
    } else {
        return lhs.name.compareTo(rhs.name);
    }
}


Và đây là cú pháp để gọi hàm này:

humans.sort(Human::compareByNameThenAge);

Kết quả khi áp dụng:

@Test
public void
  givenMethodDefinition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
     
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    humans.sort(Human::compareByNameThenAge);
    Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

 

Method signature là 4 thành phần có mặt trong cú pháp khai báo của tất cả method: return type, parameter type, số lượng parameter, thứ tự parameter

5 Sắp xếp thông qua trích xuất Comparator

Với cách này, chúng ta không cần define lại logic sắp xếp bằng cách sử dụng instance method reference và method Comparator.comparing().
 

@Test
public void
  givenInstanceMethod_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
     
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    Collections.sort(
      humans, Comparator.comparing(Human::getName));


// phần này liên quan đến test.
    assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}

 

6 Reversed Sort

JDK 8 có cung cấp một hàm tiện ích để đảo ngược lại logic sắp xếp của comparator, ta có thể dùng nó trong bài toán này:

@Test
public void whenSortingEntitiesByNameReversed_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 10), 
      new Human("Jack", 12)
    );
     
    Comparator comparator
      = (h1, h2) -> h1.getName().compareTo(h2.getName());


     
    humans.sort(comparator.reversed());

  

// có lẽ phần này không cần chú thích nữa 
    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}


7 Sắp xếp theo nhiều điều kiện khác nhau

Ta hoàn toàn có thể sử dụng lambda expression để thiết kế các phép toán sắp xếp phức tạp hơn. Cụ thể với bài toán trong ví dụ này, hãy thử sắp xếp các thực thể theo tên trước, nếu tên trùng nhau, sắp xếp theo tuổi:
 

@Test
public void whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
    List humans = Lists.newArrayList(
      new Human("Sarah", 12), 
      new Human("Sarah", 10), 
      new Human("Zack", 12)
    );
     
    humans.sort((lhs, rhs) -> {
        if (lhs.getName().equals(rhs.getName())) {
            return lhs.getAge() - rhs.getAge();
        } else {
            return lhs.getName().compareTo(rhs.getName());
        }
    });




    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

 

8 Sắp xếp theo nhiều điều kiện khác nhau - ứng dụng Composition

Từ bản JDK 8, chúng ta có thể liên kết nhiều comparator (chaining multiple comparator) để biểu diễn các logic sắp xếp phức tạp hơn.

Nghe thì rất... phức tạp, nhưng mọi thứ chỉ xoay quanh 1 dòng code, hãy đọc và hiểu:
 

@Test
public void
  givenComposition_whenSortingEntitiesByNameThenAge_thenCorrectlySorted() {
     
    List humans = Lists.newArrayList(
      new Human("Sarah", 12), 
      new Human("Sarah", 10), 
      new Human("Zack", 12)
    );
 
    humans.sort(
      Comparator.comparing(Human::getName).thenComparing(Human::getAge)
    );
     

// không đọc dòng này 
    Assert.assertThat(humans.get(0), equalTo(new Human("Sarah", 10)));
}

 

9 Tổng kết

Bài viết hôm nay khá ngắn gọn nhưng đã giới thiệu được 8 cách để biểu diễn logic sắp xếp các thực thể trong Java. Chúc các bạn học tốt!!
 

BTV. Phạm Thị Mỹ Phương
Phòng Truyền Thông ImicroSoft Hồ Chí Minh
Hotline: 0916 878 224
Email: phuongptm@imicrosoft.edu.vn

 

Bạn đang muốn tìm kiếm 1 công việc với mức thu nhập cao.
✅ Hoặc là bạn đang muốn chuyển đổi công việc mà chưa biết theo học ngành nghề gì cho tốt.
✅ Giới thiệu với bạn Chương trình đào tạo nhân sự dài hạn trong 12 tháng với những điều đặc biệt mà chỉ có tại IMIC và đây cũng chính là sự lựa chọn phù hợp nhất dành cho bạn:
👉 Thứ nhất: Học viên được đào tạo bài bản kỹ năng, kiến thức chuyên môn lý thuyết, thực hành, thực chiến nhiều dự án và chia sẻ những kinh nghiệm thực tế từ Chuyên gia có nhiều năm kinh nghiệm dự án cũng như tâm huyết truyền nghề.
👉 Thứ hai: Được ký hợp đồng cam kết chất lượng đào tạo cũng như mức lương sau tốt nghiệp và đi làm tại các đối tác tuyển dụng của IMIC. Trả lại học phí nếu không đúng những gì đã ký kết.
👉 Thứ ba: Cam kết hỗ trợ giới thiệu công việc sang đối tác tuyển dụng trong vòng 10 năm liên tục.
👉 Thứ tư: Được hỗ trợ tài chính với mức lãi suất 0 đồng qua ngân hàng VIB Bank.
👉  Có 4 Chương trình đào tạo nhân sự dài hạn dành cho bạn lựa chọn theo học. Gồm có:
1)  Data Scientist full-stack
2)  Embedded System & IoT development full-stack
3)  Game development full-stack
4)  Web development full-stack 
✅ Cảm ơn bạn đã dành thời gian lắng nghe những chia sẻ của mình. Và tuyệt vời hơn nữa nếu IMIC được góp phần vào sự thành công của bạn. 
✅ Hãy liên hệ ngay với Phòng tư vấn tuyển sinh để được hỗ trợ về thủ tục nhập học.
✅ Chúc bạn luôn có nhiều sức khỏe và thành công!

 

Tham khảo các khóa đào tạo nhân sự qua danh mục