Mengurutkan ArrayList objek menggunakan urutan pengurutan kustom


119

Saya ingin menerapkan fitur sortir untuk aplikasi buku alamat saya.

Saya ingin mengurutkan file ArrayList<Contact> contactArray. Contactadalah kelas yang berisi empat bidang: nama, nomor rumah, nomor ponsel, dan alamat. Saya ingin menyortirname .

Bagaimana cara menulis fungsi sortir kustom untuk melakukan ini?

Jawaban:


270

Berikut tutorial tentang memesan objek:

Meskipun saya akan memberikan beberapa contoh, saya akan merekomendasikan untuk tetap membacanya.


Ada berbagai cara untuk mengurutkan file ArrayList. Jika Anda ingin mendefinisikan pengurutan natural (default) , Anda harus membiarkan implementasinya . Dengan asumsi bahwa Anda ingin mengurutkan secara default , maka lakukan (nullcheck dihilangkan agar lebih mudah):ContactComparablename

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.
}

sehingga Anda bisa melakukannya

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

Collections.sort(contacts);

Jika Anda ingin menentukan pengurutan terkontrol eksternal (yang menggantikan pengurutan alami), Anda perlu membuat 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());
    }
}); 

Anda bahkan dapat menentukan Comparators Contactitu sendiri sehingga Anda dapat menggunakannya kembali daripada membuatnya kembali setiap saat:

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);
        }
    };

}

yang dapat digunakan sebagai berikut:

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);

Dan untuk menyempurnakannya, Anda dapat mempertimbangkan untuk menggunakan komparator javabean generik :

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));
    }

}

yang dapat Anda gunakan sebagai berikut:

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

(seperti yang Anda lihat di kode, mungkin bidang kosong sudah tercakup untuk menghindari NPE selama pengurutan)


2
Saya akan menambahkan kemungkinan untuk menentukan beberapa komparator sebelumnya, dan kemudian menggunakannya dengan nama ...
Stobor

2
Sebenarnya, saya baru saja melakukannya. Lebih mudah daripada mencoba menjelaskan diri sendiri.
Stobor

@BalusC: Tidak ada masalah. Saya tidak dapat menerima pujian atas gagasan itu, saya mendapatkannya dari String.CASE_INSENSITIVE_ORDERdan teman-teman, tetapi saya menyukainya. Membuat kode yang dihasilkan lebih mudah dibaca.
Stobor

1
Definisi Pembanding itu mungkin juga staticdan mungkin finaljuga ... Atau sesuatu seperti itu ..
Stobor

Heheh ... BeanComparator itu seperti Awesome On A Stick! :-) (Saya tidak ingat perbandingan logika yang tepat dari nulls, tetapi apakah ia menginginkan (o1 == null && o2 == null) ? 0 :di awal garis pengembalian itu?)
Stobor

28

Selain apa yang telah diposting, Anda harus tahu bahwa sejak Java 8 kita dapat mempersingkat kode kita dan menulisnya seperti:

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

atau karena Daftar sekarang memiliki sortmetode

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

Penjelasan:

Sejak Java 8, antarmuka fungsional (antarmuka dengan hanya satu metode abstrak - mereka dapat memiliki lebih banyak metode default atau statis) dapat dengan mudah diimplementasikan menggunakan:

Karena Comparator<T>hanya memiliki satu metode abstrakint compare(T o1, T o2) itu adalah antarmuka fungsional.

Jadi alih-alih (contoh dari jawaban @BalusC )

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

kita dapat mengurangi kode ini menjadi:

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

Kita dapat menyederhanakan lambda ini (atau yang lainnya) dengan melewatkan

  • tipe argumen (Java akan menyimpulkannya berdasarkan tanda tangan metode)
  • atau {return...}

Jadi, bukan

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

kita bisa menulis

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

Juga sekarang Comparatormemiliki metode statis seperti comparing(FunctionToComparableValue)ataucomparing(FunctionToValue, ValueComparator) yang dapat kita gunakan untuk membuat Pembanding dengan mudah yang harus membandingkan beberapa nilai tertentu dari objek.

Dengan kata lain kita dapat menulis ulang kode di atas sebagai

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

8

Halaman ini memberi tahu Anda semua yang perlu Anda ketahui tentang pengurutan koleksi, seperti ArrayList.

Pada dasarnya Anda perlu

  • buat Contactkelas Anda mengimplementasikan Comparableantarmuka dengan
    • membuat metode public int compareTo(Contact anotherContact)di dalamnya.
  • Setelah Anda melakukan ini, Anda dapat menelepon Collections.sort(myContactList);,
    • mana myContactListadalah ArrayList<Contact>(atau setiap koleksi lain Contact).

Ada cara lain juga, melibatkan pembuatan kelas Comparator, dan Anda dapat membacanya dari halaman yang ditautkan juga.

Contoh:

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 dan bguiz telah memberikan jawaban yang sangat lengkap tentang cara menggunakan Pembanding bawaan Java.

Saya hanya ingin menambahkan bahwa google-collections memiliki kelas Pemesanan yang lebih "bertenaga" daripada Pembanding standar. Mungkin layak untuk dicoba. Anda dapat melakukan hal-hal keren seperti meracik Pengurutan, membalikkannya, mengurutkan tergantung pada hasil fungsi untuk objek Anda ...

Berikut adalah postingan blog yang menyebutkan beberapa manfaatnya.


Perhatikan bahwa google-collections sekarang menjadi bagian dari Guava (library java umum Google), jadi Anda mungkin ingin bergantung pada Guava (atau modul collection Guava) jika ingin menggunakan kelas Ordering.
Etienne Neveu

4

Anda perlu membuat kelas Contact Anda mengimplementasikan Comparable , lalu mengimplementasikancompareTo(Contact) metode tersebut. Dengan begitu, Collections.sort akan dapat mengurutkannya untuk Anda. Per halaman yang saya tautkan, bandingkanTo 'mengembalikan bilangan bulat negatif, nol, atau bilangan bulat positif karena objek ini kurang dari, sama dengan, atau lebih besar dari objek yang ditentukan.'

Misalnya, jika Anda ingin mengurutkan berdasarkan nama (A sampai Z), kelas Anda akan terlihat seperti ini:

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);
    }
}

Bekerja dengan baik dengan saya, terima kasih! Saya juga menggunakan bandingkanToIgnoreCase untuk mengabaikan kasus.
Rani Kheir

3

Dengan menggunakan lambdaj Anda dapat mengurutkan koleksi kontak Anda (misalnya dengan nama mereka) sebagai berikut

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

atau dengan alamat mereka:

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

dan seterusnya. Secara umum, ia menawarkan DSL untuk mengakses dan memanipulasi koleksi Anda dalam banyak cara, seperti memfilter atau mengelompokkan kontak Anda berdasarkan beberapa kondisi, menggabungkan beberapa nilai propertinya, dll.


0

Collections.sort adalah implementasi pengurutan yang baik. Jika Anda tidak memiliki The Comparable yang diimplementasikan untuk Contact, Anda harus meneruskan implementasi Comparator

Catatan:

Algoritme pengurutan adalah gabungan yang dimodifikasi (di mana penggabungan dihilangkan jika elemen tertinggi di sub-daftar rendah lebih kecil dari elemen terendah di sub-daftar tinggi). Algoritme ini menawarkan kinerja n log (n) yang terjamin. Daftar yang ditentukan harus dapat dimodifikasi, tetapi tidak perlu diubah ukurannya. Implementasi ini membuang daftar yang ditentukan ke dalam larik, mengurutkan larik, dan mengulangi daftar yang menyetel ulang setiap elemen dari posisi yang sesuai dalam larik. Ini menghindari kinerja n2 log (n) yang akan dihasilkan dari upaya untuk mengurutkan daftar tertaut di tempat.

Jenis gabungan mungkin lebih baik daripada kebanyakan algoritma pencarian yang dapat Anda lakukan.


0

Saya melakukannya dengan cara berikut. nomor dan nama adalah dua daftar larik. Saya harus mengurutkan nama. Jika terjadi perubahan pada urutan nama arralist maka nomor arraylist juga berubah urutannya.

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);


                        }
                    }
                }
            }



}

Anda akan membutuhkan waktu lebih lama untuk menulis ini, mendapatkan kinerja pengurutan yang kurang optimal, menulis lebih banyak bug (dan semoga lebih banyak tes), dan kode akan lebih sulit untuk ditransfer ke orang lain. Jadi itu tidak benar. Ini mungkin berhasil, tetapi itu tidak membuatnya benar.
Eric

0

gunakan metode ini:

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;
}

`

dan penggunaan: mySortedlist = sortList(myList); Tidak perlu menerapkan pembanding di kelas Anda. Jika Anda ingin membalikkan order swap 1dan-1


0

Oke, saya tahu ini sudah lama dijawab ... tapi, ini beberapa info baru:

Misalnya kelas Contact yang dimaksud sudah memiliki pengurutan natural yang ditentukan melalui implementasi Comparable, tetapi Anda ingin menimpa pengurutan itu, misalnya dengan nama. Inilah cara modern untuk melakukannya:

List<Contact> contacts = ...;

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

Dengan cara ini ia akan mengurutkan berdasarkan nama terlebih dahulu (dalam urutan terbalik), dan kemudian untuk tabrakan nama, ia akan kembali ke urutan 'alami' yang diterapkan oleh kelas Contact itu sendiri.


-1

Anda harus menggunakan fungsi Arrays.sort. Kelas yang berisi harus mengimplementasikan Comparable.


Itulah mengapa saya mengatakan Array.
biarawan

Masalahnya adalah OP menggunakan ArrayList, bukan array.
Pshemo
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.