Java 8 Comparator, Làm thế nào để sắp xếp List

Java 8 Comparator, làm thế nào để sắp xếp List

Bạn muốn sắp xếp một List của bạn, hãy tìm hiểu bạn sẽ có nhiều cách để có thể có được danh sách mình mong muốn. Trong bài viết này chúng ta sẽ thấy vài ví dụ về cách sắp xếp một danh sách trong Java 8

Sắp xếp một List theo Alphabetically

List<String> cities = Arrays.asList(
       "Milan",
       "london",
       "San Francisco",
       "Tokyo",
       "New Delhi"
);
System.out.println(cities);
//[Milan, london, San Francisco, Tokyo, New Delhi]


cities.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(cities);
//[london, Milan, New Delhi, San Francisco, Tokyo]

cities.sort(Comparator.naturalOrder());
System.out.println(cities);
//[Milan, New Delhi, San Francisco, Tokyo, london]

Trên bạn sẽ thấy, tôi đã cố tình viết chữ 「l」thường, nếu trong so sánh CASE_INSENSITIVE_ORDER thì sẽ không phân biệt chữ hoa chữ thường, Nhưng kiểu naturalOrder() thì có phần biệt chữ hoa chữ thường Về cơ bản trong java 7 thì chúng ta có: Collections.sort() đẻ sắp xếp một List Nhưng trong java 8 thì chúng ta sử dụng List.sort() và comparator đế sắp xếp một List

Sắp xếp một List các số nguyên

List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
System.out.println(numbers); //[6, 2, 1, 4, 9]

numbers.sort(Comparator.naturalOrder());
System.out.println(numbers); //[1, 2, 4, 6, 9]

Sắp xếp một danh sách bởi thuộc tính kiểu String

Giả sử chúng ta có một danh sách Movies , chúng ta muốn sắp xếp theo title. Chúng ta có thể sử dụng Comparator.comparing() và truyền vào biến là thuộc tính title để thực hiện sắp xếp

List<Movie> movies = Arrays.asList(
        new Movie("Lord of the rings"),
        new Movie("Back to the future"),
        new Movie("Carlito's way"),
        new Movie("Pulp fiction"));
movies.sort(Comparator.comparing(Movie::getTitle));
movies.forEach(System.out::println);

// Output
/*
Movie{title='Back to the future'}
Movie{title='Carlito's way'}
Movie{title='Lord of the rings'}
Movie{title='Pulp fiction'}
*/

Nhìn trông source có vẻ đơn giản vì không sử dụng so sánh trung gian nào, nhưng thực sự thì bạn vào source code implement của Comparator.comparing() bạn sẽ thấy

return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));

Về cơ bản trước ở java 7 thì bạn phải tạo Comparator để so sanh, nhưng lên java 8 có nhiều trường hợp so sánh đơn giản đã được tích hợp sẵn

Sắp xếp List bởi thuộc tính kiểu double

Bằng cách tương tự, chúng ta có thể sử dụng Comparator.comparingDouble() để so sánh giá trị doublt. Trong ví dụ sau chúng ta muốn sắp xếp danh sách các bộ phim theo thứ tự Rating từ cao đến thấp

List<Movie> movies = Arrays.asList(
        new Movie("Lord of the rings", 8.8),
        new Movie("Back to the future", 8.5),
        new Movie("Carlito's way", 7.9),
        new Movie("Pulp fiction", 8.9));
movies.sort(Comparator.comparingDouble(Movie::getRating)
                      .reversed());
movies.forEach(System.out::println);

Chúng ta sử dụng hàm reversed để làm đảo ngược thứ tự sắp xếp thông thường từ thấp đến cao -> Từ cao đến thấp, bên cạnh sử dụng so sanh theo double bạn cũng có thể sử dụng so sánh theo kiểu Int hoặc Long bằng cách sử dụng comparingInt(0 hoặc comparingLong()

Sắp xếp danh sách bởi Custom Comparator

Trong các ví dụ trước chúng ta đều dùng kiểu mặc định của java 8 mà không định nghĩa kiểu Comparator, trong ví dụ sau chúng ta muốn sắp xếp các bộ phim theo dấu Starred, nghĩa là những bộ phim có Start sẽ đứng ở trên

List<Movie> movies = Arrays.asList(
        new Movie("Lord of the rings", 8.8, true),
        new Movie("Back to the future", 8.5, false),
        new Movie("Carlito's way", 7.9, true),
        new Movie("Pulp fiction", 8.9, false));

// Định nghĩa một kiểu comparator
movies.sort(new Comparator<Movie>() {
    @Override
    public int compare(Movie m1, Movie m2) {
        if(m1.getStarred() == m2.getStarred()){
            return 0;
        }
        return m1.getStarred() ? -1 : 1;
     }
});
movies.forEach(System.out::println);

// Output
/*
Movie{starred=true, title='Lord of the rings', rating=8.8}
Movie{starred=true, title='Carlito's way', rating=7.9}
Movie{starred=false, title='Back to the future', rating=8.5}
Movie{starred=false, title='Pulp fiction', rating=8.9}
*/

Trên là cách thông thường, chúng tao có thể sử dụng lamda với một class ẩn danh để viết

movies.sort((m1, m2) -> {
    if(m1.getStarred() == m2.getStarred()){
        return 0;
    }
    return m1.getStarred() ? -1 : 1;
});

Cũng có thể sử dụng Comparator.comparing()

movies.sort(Comparator.comparing(Movie::getStarred, (star1, star2) -> {
    if(star1 == star2){
         return 0;
    }
    return star1 ? -1 : 1;
}));

Trên là những ví dụ chỉ so sánh với một thuộc tinh, sau đây chúng ta sẽ đi đến ví dụ so sánh với danh sách các comparator

Sắp xếp List với một danh sách các comparator

List<Movie> movies = Arrays.asList(
        new Movie("Lord of the rings", 8.8, true),
        new Movie("Back to the future", 8.5, false),
        new Movie("Carlito's way", 7.9, true),
        new Movie("Pulp fiction", 8.9, false));

// Sắp xếp
movies.sort(Comparator.comparing(Movie::getStarred)
                      .reversed()
                      .thenComparing(Comparator.comparing(Movie::getRating)
                      .reversed())
);
movies.forEach(System.out::println);

// Output
/*
Movie{starred=true, title='Lord of the rings', rating=8.8}
Movie{starred=true, title='Carlito's way', rating=7.9}
Movie{starred=false, title='Pulp fiction', rating=8.9}
Movie{starred=false, title='Back to the future', rating=8.5}
*/

Sau khi sắp xếp bạn có được danh sách theo starred và rating theo thứ tự giảm dần

Bạn có thể sử dụng một hoặc nhiều comparator trong hàm sort để có được List mong muốn của mình

Tổng kết

Trong Java 8 bạn có thể sủ dụng 2 kiểu so sánh đó là như ở trong Java 7 hoặc sử dụng viết so sánh với Lamda để có List mình mong muốn