Trang

Saturday, June 14, 2014

Comparable: Java

Khi làm việc với lượng lớn dữ liệu thì nhu cầu sắp xếp để tìm kiếm càng lớn, do đó chúng ta thấy tính năng sắp xếp có ở hầu hết các ứng dụng. Nhưng nếu bạn tự code thì sẽ mất khá nhiều thời gian và công sức. Ở bài viết này, tôi sẽ hướng dẫn bạn một cách đơn giản để sắp xếp dữ liệu trong Java.
Trong bài viết này, tôi sẽ sử dụng java.util.Collections.sort để sắp xếp các đối tượng trong Collection theo thứ tự tăng dần. Để sắp xếp được, thì trước tiên các đối tượng đó phải implement interface java.lang.Comparable với phương thức compareTo(Object obj2).
Giả sử ta cần so sánh đối tượng obj1 với obj2 thì phương thức này phải trả về kết quả như sau:
  • 0 Nếu như obj1 và obj2 bằng nhau
  • Số dương Nếu obj1 lớn hơn obj2
  • Số âm Nếu obj1 nhỏ hơn obj2

Ví dụ 1: Sắp xếp một danh sách chuỗi theo thứ tự tăng dần

Đây là code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
    //Khởi tạo một đối tượng List chứa tên là các chuỗi
    List<String> names = new LinkedList<String>();
 
    //Khởi tạo dữ liệu
    names.add("Tạp");
    names.add("Chí");
    names.add("Lập");
    names.add("Trình");
 
    //Sắp xếp
    java.util.Collections.sort(names);
 
    //Hiển thị xâu sau khi sắp xếp
    for (String name : names) {
        System.out.println(name);
    }
}
Và đây là kết quả
Chí
Lập
Trình
Tạp

Ví dụ 2: sắp xếp danh sách sản phẩm theo tứ tự giá tăng dần

Đầu tiên, ta viết class Product để mô tả sản phẩm.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Product{
 
    private int code;
    private String name;
    private double price;
 
    public Product(int code, String name, double price) {
        this.code = code;
        this.name = name;
        this.price = price;
    }
 
    public int getCode() {
        return code;
    }
 
    public void setCode(int code) {
        this.code = code;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
 
    @Override
    public String toString() {
        return "Product{ " + code + ", " + name + ", " + price + '}';
    }
}
Muốn sắp xếp được thì ta implement phương thức compareTo() của interface java.util.Comparable. Ví dụ ta so sánh hai đối tượng product1 với product2 thì phương thức compareTo(Product product2) trả về:
  • 0 nếu product1 và product2 có giá bằng nhau.
  • Số dương nếu product1 có giá lớn hơn giá của product2.
  • Số âm nếu product1 có giá nhỏ hơn giá của product2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Product implements Comparable<Product> {
    //....
    @Override
    public int compareTo(Product o) {
        //this case we compare product by price
 
        //this product and o are the same
        if (this.price == o.price) {
            return 0;
        }
        //this product is greater than o
        if (this.price > o.price) {
            return 1;
        }
        //this product is less than o
        return -1;
    }
}
Và mã để sắp xếp danh sách Product
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
    //Instantiate an object of list product
    List<Product> products = new LinkedList<Product>();
 
    //Prepare mock data to test
    products.add(new Product(1, "TV", 300));
    products.add(new Product(2, "Iphone", 600));
    products.add(new Product(3, "Sony Vaio E", 200));
    products.add(new Product(4, "Asus E", 800));
 
    //Sort list
    java.util.Collections.sort(products);
 
    //print sorted list
    for (Product p : products) {
        System.out.println(p);
    }
}
Kết quả:
Product{code=3, name=Sony Vaio E, price=200.0}
Product{code=1, name=TV, price=300.0}
Product{code=2, name=Iphone, price=600.0}
Product{code=4, name=Asus E, price=800.0}

Ví dụ này mới chỉ hướng dẫn bạn sắp xếp một List, nhưng bạn hoàn toàn có thể sắp xếp bất kỳ đối tượng Collection nào. Nếu bạn muốn sắp xếp một mảng thì có thể dùng phương thức sort củajava.util.Arrays.

Kết luận

Như vậy, bạn đã hoàn thành phần sắp xếp một cách đơn giản và dễ dàng. Nhưng đây mới chỉ là sắp xếp theo một tiêu chí, vậy nếu ta muốn sắp xếp theo nhiều tiêu chí (ví dụ: sắp xếp theo tên và giá) thì làm thế nào? Tôi sẽ giúp bạn trả lời câu hỏi này ở bài viết sau.

1. Comparable có phải là một type như int, String, boolean?
2. Sự khác nhau giữa type int và class type Integer? (chỉ là vd cho những trường hợp tương tự)
3. Làm sao để tạo được một variable type Comparable?
4. Comparable có phải luôn luôn chi là một Interface để implement?

Thanks a lot.

1. Comparable là một interface. Một interface là một Object. Lớp implements interface này phải implements các phương thức nó có dùng để so sánh: compare(Object o1, Object o2). Cơ chế đa hình trong Java lợi dụng điểm mạnh của interface đưa ra một thuật toán sắp xếp rất hiệu quả dựa trên những đối tượng có thể so sánh (Comparable) hơn là cho người dùng tự implements thuật toán.

́2. Integer là wapper (lớp bọc) của kiểu int. Trong trường hợp mình cần một đối tượng Integer, ví dụ: một collection ko thể nào chứa một kiểu primary được, cần phải có một lớp thay thế và dễ dàng lấy lại được giá trị gốc (primative). Khi đó lớp Integer thích hợp hơn là int.

Ví dụ về lớp bọc:

class MyInt {

     int i;

     public MyInt(int i);



     int getValue();

     // no setter, it should be an immutable object
}


3. Đơn giản: 
class NewObject implements Comparable { 

       public int compare(Object o1, Object o2) {

              // if o1 less than o2 return -1

              // if o1 greater than o2 return 1

              // return 0; 

     }
}

Chú ý: Hầu hết các Wrapper có sẵn Double, Integer, String, Float.. đều là Comparable. "Please check API for details"

4. Mình kô hiểu ý này lắm.. Tuy nhiên, 3 câu trả lời đủ để bạn tìm thấy những gì bạn muốn về Comparable interface

Cám ơn bạn, có lẽ mình phải đọc kỹ hơn về phần lớp bọc, vì thật sự mình cũng chưa hiểu rõ lắm. 

1. word.compareTo(data) nghĩa là word < data hay word > data?
2. x==null thì có cần khai báo package nào k? vì mình viết như bạn nó báo lỗi ở null :)

Bạn cần khai báo một lớp mới vì không lớp String không cho phép extend:

class MyString implements Comparable<MyString> {
    String data = "";
    public int compareTo(MyString thatObj) {
        if (data.length < thatObj.data.length) { // so sánh độ dài thôi nhé
            return -1;
        }
        else if (data.length == thatObj.data.length) {
           return 0;
        }
        else {
            return 1;
        }
    }
}

So sánh String, hay chính xác hơn là so sánh 2 đối tượng kiểu String, chúng ta hoàn toàn có thể dùng các phương thức có sẵn trong class String:
cách 1 : dùng compareTo(Object object) - phương thức này có do class String implements interface Comparable<String>, nó đã implements thì người viết đã định nghĩa phương thức này rồi, nên chỉ cần dùng - cách dùng thế nào bạn tra cứu trên java se 7 api sẽ rõ.
cách 2 : dùng equals(Object obj) - Phương thức này kế thừa của lớp Object và đã được override để dùng được cho String 

Comparable và Comparator, về cơ bản thì 2 interface này đều có tác dụng làm cho các đối tượng thuộc class implements chúng có thể so sánh được xa hơn có thể là sắp xếp 1 list các đối tượng, tất nhiên là theo một tiêu chí mà mong muốn nào đó, Comparable có method compareTo(Object obj), Comparator co method compare(Object obj, Object otherObj), mà các class implements chúng cần định nghĩa.

Có một sự khác biệt lớn giữa việc sử dụng của 2 Interface này.
Thứ nhất nếu dùng Comparable thì class implements nó chỉ có thể so sánh các được các đối tượng của class đó theo một tiêu chí cố định.

Đối với việc dùng Comparator, ta có thể sánh các đối tượng thuộc class theo một số tiêu chí khác nhau, nếu class có 2 thuộc tính
là age, weight. Cách 1 nếu age lớn hơn thì đối tượng lớn hơn, cách 2 nếu weight lớn hơn thì đối tượng lớn hơn. trong khi nếu dùng
Comparable thì chỉ có thể dùng theo một quy tắc cố định (có thể chỉ xét về cân nặng hoặc tuổi, hoặc ưu tiên tuổi trước sau nếu ai cùng tuổi thì xét cân nặng và vice versa). dĩ nhiên là cách sử dụng của chúng khác nhau, bản thân lớp cần so sánh không implement Comparator,

Trong trường hợp này việc dùng Comparator hay Comparable không có gì khác biệt, vì chỉ so sánh theo một tiêu chí.
Còn theo quan điểm của mình thì dùng Comparable trong trường hợp trên có vẻ rõ ràng hơn. 

Lý do mình phản đối dùng Comparable là vì khi sử dụng cho trường hợp trên sẽ phải tạo đối tượng cho MyString, tốn bộ nhớ và tài nguyên. Dùng Comparable chỉ nên dùng khi lớp do bản thân thiết kế / viết ra, hoặc có thể kế thừa được, còn nếu không thì chỉ nên viết Comparator thôi, vì Comparator có thể sử dụng lại nhiều lần, cũng không cần tạo wrap object cho đối tượng có sẵn.

Ví dụ khi so sánh String s1 và s2 dùng MyString (comparable):
(new MyString(s1)).compareTo(new MyString(s2))

Nếu dùng Comparator:
class MyComparator implements Comparator<String> {
public int compare(String s1, String s2) {...}
}
...
MyComparator comp = new MyComparator();
...
comp.compare(s1, s2)
Nếu chỉ đơn giản chỉ là so sánh 2 String thì chẳng cần dùng Comparator làm gì, vì bản thân class String đã implements Comparable nên có thể dùng
s1.compareTo(s2), bắt cứ khi nào, và cũng có thể dùng s1.equals(s2). Comparator và Comparable chỉ dùng cho việc so sánh các đối của class mình tự viết hay tổng quát là class mà đối tượng chưa thể so sánh được với nhau, ( nếu so sánh được rồi viết theo cách khác với Comparator) 

No comments:

Post a Comment