Hiểu về ArrayList sorting và Comparator

Để hiểu rõ bài viết này, mình mong các bạn đọc trước bài lambda expression. Vì trong bài này chúng ta sẽ dùng nhiều đến lambda expression để triển khai Comparator.

Comparator là gì?

Comparator là một Functional interface, được sử dụng để sắp xếp các object của user định nghĩa(Nghĩa là các class được các dev chúng ta định nghĩa đấy). 

Functional interface là một interface có duy nhất một abstract method. Comparator là một functional interface có method compare()  là abstract method.

int compare(T o1, T o2);

Nếu vào chúng ta vào xem phần định nghĩa của Comparator thì chúng ta sẽ ngạc nhiên rằng “oh nó có đến 2 abstract method”?

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
    ...
}

Note: Các default method không phải là abstract method, nó là một khái niệm mới được giới thiệu trong java 8, bản thân những method này có implement riêng của nó.

Quay lại, ủa tại sao tui nói functional interface chỉ có một abstract method, sao giờ lại có 2 abstract method thế kia?

Um, thật ra thì

boolean equals(Object obj);

Không phải là một abstract method. Xem kỹ lại một chút document oracle, người ta có nói rõ rằng:

Nếu một interface khai báo một abstract method overriding một trong những public method của java.lang.Object thì nó sẽ không được tính là một abstract method của nó.

Ở đây, equals() là một public của java.lang.Object nên nó sẽ được bỏ qua, và chúng ta sẽ chỉ có duy nhất một abstract method trong Comparator compare().

Comparator và sorting

Nếu các bạn đã học qua các thuật toán sắp xếp, về bản chất để quyết định vị trí của hai phần tử trong danh sách sẽ phụ thuộc vào việc so sánh giá trị của chúng.

Chúng ta có các hàm sort() được cung cấp sẵn như Arrays.sort(), và Collections.sort(). Tất cả chúng về bản chất vẫn sẽ sử dụng các thuật toán sắp xếp mà chúng ta có, họ có thể cải tiến, nhưng bản chất là vậy!

Comparator với method compare(T o1, T o2) sẽ giúp chúng ta quy định lại cách mà các phần tử trong danh sách được đem đi so sánh.

Tại sao lại cần đến Comparator?

Giả sử chúng ta định nghĩa một class Student gồm các thuộc tính name, age, score, address. 

public class Student {
    private String name;
    private int age;
    private double score;
    private String address;

    public Student(String name, int age, double score, String address) {
        this.name = name;
        this.age = age;
        this.score = score;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                ", address='" + address + '\'' +
                '}';
    }

}

Chúng ta có một ArrayList chứa các Student. Với object Student có rất nhiều thuộc tính như vậy? Thì việc sắp xếp sẽ tiến hành như thế nào? 

Để sắp xếp các Student chứa trong ArrayList chúng ta cần làm các bước sau:

  1. Liệt kê các thuộc tính muốn sắp xếp
  2. Sử dụng các method sort() java cung cấp sẵn và implemet Comparator (tất nhiên là implement method compare(T o1, T o2).

Ví dụ: Cho một ArrayList<Student> students.

Sắp xếp students theo name

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("b", 18, 5.5, "DN"));
        students.add(new Student("a", 20, 9.5, "BF"));
        students.add(new Student("b", 15, 7.5, "HF"));
        students.add(new Student("c", 21, 3.5, "SF"));

        students.sort(((o1, o2) -> o1.getName().compareTo(o2.getName())));

        // Collections sort
        Collections.sort(students, ((o1, o2) -> o1.getName().compareTo(o2.getName())));

        students.forEach(s -> System.out.println(s.toString()));
    }

}

Output:

Student{name=’a’, age=20, score=9.5, address=’BF’}
Student{name=’b’, age=18, score=5.5, address=’DN’}
Student{name=’b’, age=15, score=7.5, address=’HF’}
Student{name=’c’, age=21, score=3.5, address=’SF’}

Sắp xếp theo name và score, thứ tự ưu tiên name -> score

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("b", 18, 5.5, "DN"));
        students.add(new Student("a", 20, 9.5, "BF"));
        students.add(new Student("b", 15, 7.5, "HF"));
        students.add(new Student("c", 21, 3.5, "SF"));

        students.sort(Comparator.comparing(o -> ((Student)o).getName())
                .thenComparing(o -> ((Student)o).getScore()));

        // Collections sort
        Collections.sort(students, Comparator.comparing(o -> ((Student)o).getName())
                .thenComparing(o -> ((Student)o).getScore()));


        students.forEach(s -> System.out.println(s.toString()));
    }

}

Output:Student{name=’a’, age=20, score=9.5, address=’BF’}
Student{name=’b’, age=18, score=5.5, address=’DN’}
Student{name=’b’, age=15, score=7.5, address=’HF’}
Student{name=’c’, age=21, score=3.5, address=’SF’}

Note:

thenComparing() là một default method trong Comparator giúp chúng ta sắp xếp theo nhiều tiêu chí.

 Về bản chất thì sort() của ArrayList hay Collections.sort() đều như nhau, nên các bạn có thể tuỳ ý chọn.

‹Previous Next›

5 1 vote
Article Rating
Subscribe
Notify of
guest
2 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
2
0
Would love your thoughts, please comment.x
()
x