Sắp xếp một Mảng Danh sách các đối tượng sử dụng thứ tự sắp xếp tùy chỉnh


119

Tôi đang tìm cách triển khai tính năng sắp xếp cho ứng dụng sổ địa chỉ của mình.

Tôi muốn sắp xếp một ArrayList<Contact> contactArray. Contactlà một lớp chứa bốn trường: tên, số nhà, số điện thoại di động và địa chỉ. Tôi muốn sắp xếp name.

Làm cách nào để viết hàm sắp xếp tùy chỉnh để thực hiện việc này?

Câu trả lời:


270

Đây là hướng dẫn về cách sắp xếp các đối tượng:

Mặc dù tôi sẽ đưa ra một số ví dụ, nhưng dù sao thì tôi cũng khuyên bạn nên đọc nó.


Có nhiều cách khác nhau để sắp xếp một ArrayList. Nếu bạn muốn xác định một tự nhiên (mặc định) đặt hàng , sau đó bạn cần phải cho Contactthực hiện Comparable. Giả sử rằng bạn muốn sắp xếp theo mặc định name, sau đó thực hiện (bỏ qua nullcheck để đơn giản hơn):

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

để bạn có thể làm

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

Nếu bạn muốn xác định một thứ tự có thể kiểm soát bên ngoài (sẽ ghi đè thứ tự tự nhiên), thì bạn cần tạo Comparator:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Bạn thậm chí có thể xác định các Comparators trong Contactchính nó để bạn có thể sử dụng lại chúng thay vì tạo lại chúng mọi lúc:

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

có thể được sử dụng như sau:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

Và để đánh dấu phần đầu, bạn có thể cân nhắc sử dụng bộ so sánh javabean chung :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

mà bạn có thể sử dụng như sau:

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(như bạn thấy trong mã, các trường rỗng có thể đã được che để tránh NPE trong khi sắp xếp)


2
Tôi muốn thêm khả năng trước xác định một vài bộ so sánh, và sau đó sử dụng chúng theo tên ...
Stobor

2
Trong thực tế, tôi chỉ làm. Dễ dàng hơn là cố gắng giải thích bản thân mình.
Stobor

@BalusC: Không có probs. Tôi không thể ghi nhận ý tưởng này, tôi lấy nó từ String.CASE_INSENSITIVE_ORDERvà bạn bè nhưng tôi thích nó. Làm cho mã kết quả dễ đọc hơn.
Stobor

1
Những định nghĩa sánh nên có lẽ cũng được staticvà có lẽ finalquá ... Hoặc một cái gì đó như thế ..
Stobor

Heheh ... BeanComparator giống như Awesome On A Stick! :-) (tôi không nhớ chính xác so sánh logic của null, nhưng nó muốn có một (o1 == null && o2 == null) ? 0 :lúc bắt đầu của dòng lợi nhuận?)
Stobor

28

Ngoài những gì đã được đăng, bạn nên biết rằng kể từ Java 8, chúng tôi có thể rút ngắn mã của mình và viết nó như:

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

hoặc vì Danh sách bây giờ có sortphương thức

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

Giải trình:

Kể từ Java 8, các giao diện chức năng (giao diện chỉ có một phương thức trừu tượng - chúng có thể có nhiều phương thức mặc định hoặc tĩnh hơn) có thể được triển khai dễ dàng bằng cách sử dụng:

Comparator<T>chỉ có một phương thức trừu tượng int compare(T o1, T o2)đó là giao diện chức năng.

Vì vậy, thay vì (ví dụ từ câu trả lời @BalusC )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

chúng ta có thể giảm mã này thành:

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

Chúng tôi có thể đơn giản hóa lambda này (hoặc bất kỳ) bằng cách bỏ qua

  • các kiểu đối số (Java sẽ suy ra chúng dựa trên chữ ký của phương thức)
  • hoặc {return...}

Vì vậy, thay vì

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

chúng tôi có thể viết

(one, other) -> one.getAddress().compareTo(other.getAddress())

Ngoài ra, bây giờ Comparatorcó các phương thức tĩnh như comparing(FunctionToComparableValue)hoặc comparing(FunctionToValue, ValueComparator)mà chúng ta có thể sử dụng để dễ dàng tạo các Bộ so sánh sẽ so sánh một số giá trị cụ thể từ các đối tượng.

Nói cách khác, chúng ta có thể viết lại đoạn mã trên dưới dạng

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).

8

Trang này cho bạn biết tất cả những gì bạn cần biết về việc sắp xếp các bộ sưu tập, chẳng hạn như ArrayList.

Về cơ bản bạn cần

  • làm cho Contactlớp của bạn triển khai Comparablegiao diện bằng cách
    • tạo một phương thức public int compareTo(Contact anotherContact)bên trong nó.
  • Sau khi thực hiện việc này, bạn chỉ cần gọi Collections.sort(myContactList);,
    • nơi myContactListArrayList<Contact>(hoặc bất kỳ bộ sưu tập khác của Contact).

Cũng có một cách khác, liên quan đến việc tạo lớp So sánh và bạn cũng có thể đọc về điều đó từ trang được liên kết.

Thí dụ:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5

BalusC và bguiz đã đưa ra câu trả lời rất đầy đủ về cách sử dụng Bộ so sánh tích hợp của Java.

Tôi chỉ muốn nói thêm rằng google-collection có một lớp Đặt hàng "mạnh mẽ" hơn các lớp So sánh tiêu chuẩn. Nó có thể đáng để kiểm tra. Bạn có thể làm những việc thú vị như ghép Orderings, đảo ngược chúng, sắp xếp thứ tự tùy thuộc vào kết quả của một hàm cho các đối tượng của bạn ...

Đây là một bài đăng trên blog đề cập đến một số lợi ích của nó.


Lưu ý rằng google-collection hiện là một phần của Guava (các thư viện java phổ biến của Google), vì vậy bạn có thể muốn phụ thuộc vào Guava (hoặc mô-đun bộ sưu tập của Guava) nếu bạn muốn sử dụng lớp Đặt hàng.
Etienne Neveu

4

Bạn cần thực hiện các lớp Liên hệ của mình triển khai Có thể so sánh , và sau đó triển khai compareTo(Contact)phương thức. Bằng cách đó, Collections.sort sẽ có thể sắp xếp chúng cho bạn. Trên mỗi trang mà tôi đã liên kết đến, CompareTo 'trả về một số nguyên âm, không hoặc một số nguyên dương vì đối tượng này nhỏ hơn, bằng hoặc lớn hơn đối tượng được chỉ định.'

Ví dụ: nếu bạn muốn sắp xếp theo tên (A đến Z), lớp của bạn sẽ trông như thế này:

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

Làm việc tốt với tôi, cảm ơn! Tôi cũng đã sử dụng CompareToIgnoreCase để bỏ qua trường hợp.
Rani Kheir

3

Bằng cách sử dụng lambdaj, bạn có thể sắp xếp tập hợp các liên hệ của mình (ví dụ theo tên của họ) như sau

sort(contacts, on(Contact.class).getName());

hoặc theo địa chỉ của họ:

sort(contacts, on(Contacts.class).getAddress());

và như thế. Nói chung, nó cung cấp một DSL để truy cập và thao tác các bộ sưu tập của bạn theo nhiều cách, như lọc hoặc nhóm các địa chỉ liên hệ của bạn dựa trên một số điều kiện, tổng hợp một số giá trị thuộc tính của chúng, v.v.


0

Collections.sort là một triển khai sắp xếp tốt. Nếu bạn không có Triển khai có thể so sánh cho Liên hệ, bạn sẽ cần chuyển trong triển khai Trình so sánh

Chú ý:

Thuật toán sắp xếp là một hợp nhất được sửa đổi (trong đó phép hợp nhất bị bỏ qua nếu phần tử cao nhất trong danh sách con thấp hơn phần tử thấp nhất trong danh sách con cao). Thuật toán này cung cấp hiệu suất n log (n) được đảm bảo. Danh sách được chỉ định phải có thể sửa đổi được, nhưng không cần thay đổi kích thước. Việc triển khai này kết xuất danh sách đã chỉ định vào một mảng, sắp xếp mảng và lặp lại danh sách đặt lại từng phần tử từ vị trí tương ứng trong mảng. Điều này tránh hiệu suất n2 log (n) sẽ dẫn đến việc cố gắng sắp xếp một danh sách được liên kết tại chỗ.

Sắp xếp hợp nhất có lẽ tốt hơn hầu hết các thuật toán tìm kiếm mà bạn có thể làm.


0

Tôi đã làm điều đó bằng cách sau đây. số và tên là hai danh sách mảng. Tôi phải sắp xếp tên. Nếu bất kỳ thay đổi nào xảy ra với thứ tự arralist tên thì danh sách mảng số cũng thay đổi thứ tự của nó.

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}

Bạn sẽ mất nhiều thời gian hơn để viết điều này, nhận được hiệu suất sắp xếp kém tối ưu hơn, viết nhiều lỗi hơn (và hy vọng sẽ có nhiều thử nghiệm hơn) và mã sẽ khó chuyển cho người khác hơn. Vì vậy, nó không đúng. Nó có thể hoạt động, nhưng nó không làm cho nó chính xác.
Eric

0

sử dụng phương pháp này:

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

`

và sử dụng: mySortedlist = sortList(myList); Không cần triển khai trình so sánh trong lớp của bạn. Nếu bạn muốn hoán đổi thứ tự ngược lại 1-1


0

Được rồi, tôi biết câu này đã được trả lời từ lâu ... nhưng, đây là một số thông tin mới:

Giả sử lớp Liên hệ được đề cập đã có một thứ tự tự nhiên được xác định thông qua triển khai So sánh, nhưng bạn muốn ghi đè thứ tự đó, hãy nói theo tên. Đây là cách hiện đại để làm điều đó:

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

Bằng cách này, nó sẽ sắp xếp theo tên trước (theo thứ tự ngược lại), và sau đó đối với các va chạm tên, nó sẽ trở lại thứ tự 'tự nhiên' được thực hiện bởi chính lớp Liên hệ.


-1

Bạn nên sử dụng hàm Arrays.sort. Các lớp chứa nên triển khai So sánh.


Đó là lý do tại sao tôi nói Mảng.
tu sĩ

Vấn đề là OP đang sử dụng ArrayList, không phải mảng.
Pshemo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.