Cara mengurutkan Daftar objek berdasarkan beberapa properti

145

Saya memiliki kelas sederhana

public class ActiveAlarm {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;
}

dan List<ActiveAlarm>con. Bagaimana cara mengurutkan dalam urutan naik timeStarted, lalu dengan timeEnded? Adakah yang bisa membantu? Saya tahu di C ++ dengan algoritma generik dan operator overload <, tapi saya baru di Jawa.

Jennifer
sumber
Jawaban oleh @Yishai dalam posting ini menunjukkan penggunaan enum yang elegan untuk pengurutan khusus dan pengurutan berkelompok (beberapa argumen) menggunakan rantai pembanding.
gunalmel

Jawaban:

141

Entah ActiveAlarmmenerapkan Comparable<ActiveAlarm>atau mengimplementasikan Comparator<ActiveAlarm>dalam kelas yang terpisah. Lalu hubungi:

Collections.sort(list);

atau

Collections.sort(list, comparator);

Secara umum, itu ide yang baik untuk melaksanakan Comparable<T>jika ada "alami" urutan tunggal ... jika tidak (jika Anda kebetulan ingin menyortir dalam urutan tertentu, tetapi mungkin sama-sama mudah ingin yang berbeda) lebih baik untuk menerapkan Comparator<T>. Situasi khusus ini bisa berjalan baik, jujur ​​... tapi saya mungkin akan tetap dengan opsi yang lebih fleksibel Comparator<T>.

EDIT: Contoh implementasi:

public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
  @Override
  public int compare(ActiveAlarm x, ActiveAlarm y) {
    // TODO: Handle null x or y values
    int startComparison = compare(x.timeStarted, y.timeStarted);
    return startComparison != 0 ? startComparison
                                : compare(x.timeEnded, y.timeEnded);
  }

  // I don't know why this isn't in Long...
  private static int compare(long a, long b) {
    return a < b ? -1
         : a > b ? 1
         : 0;
  }
}
Jon Skeet
sumber
1
Fungsi bandingkan () kemungkinan tidak dalam Long karena implementasinya bahkan lebih sepele: return a - b;
papercrane
5
@ papercrane: Tidak, itu gagal karena alasan meluap. Pertimbangkan a = Long.MIN_VALUE, b = 1.
Jon Skeet
3
pada API 19 (KitKat) Long sekarang memiliki.compare
Martin Marconcini
123

Menggunakan Comparator

Sebagai contoh:

class Score {

    private String name;
    private List<Integer> scores;
    // +accessor methods
}

    Collections.sort(scores, new Comparator<Score>() {

        public int compare(Score o1, Score o2) {
            // compare two instance of `Score` and return `int` as result.
            return o2.getScores().get(0).compareTo(o1.getScores().get(0));
        }
    });

Dengan Java 8 dan seterusnya, Anda cukup menggunakan ekspresi lambda untuk mewakili instance Comparator.

Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Jigar Joshi
sumber
1
Apa yang compareTo()harus dilakukan Dari mana asalnya? Di mana saya harus mendefinisikannya?
Asqiir
1
@ Asqiir getScores()adalah pengambil untuk scoresyang merupakan List<Integer>. Ketika Anda melakukannya, getScores().get(0)Anda mendapatkan Integerobjek. Integersudah compareTo(anotherInteger)menerapkan metode, Anda tidak perlu mendefinisikannya.
walen
apa yang didapat (0)?
Ali Khaki
1
Terima kasih berfungsi seperti pesona (saya menggunakannya tanpa bagian .get (0)). Bagaimana saya bisa membalik urutan?
53

JAVA 8 dan Jawaban Di Atas (Menggunakan Ekspresi Lambda)

Di Java 8, ekspresi Lambda diperkenalkan untuk membuat ini lebih mudah! Alih-alih membuat objek Comparator () dengan semua perancahnya, Anda dapat menyederhanakannya sebagai berikut: (Menggunakan objek Anda sebagai contoh)

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);

atau bahkan lebih pendek:

Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));

Satu pernyataan itu setara dengan yang berikut:

Collections.sort(list, new Comparator<ActiveAlarm>() {
    @Override
    public int compare(ActiveAlarm a1, ActiveAlarm a2) {
        return a1.timeStarted - a2.timeStarted;
    }
});

Pikirkan ekspresi Lambda sebagai hanya mengharuskan Anda untuk memasukkan bagian yang relevan dari kode: tanda tangan metode dan apa yang dikembalikan.

Bagian lain dari pertanyaan Anda adalah bagaimana membandingkan beberapa bidang. Untuk melakukannya dengan ekspresi Lambda, Anda dapat menggunakan .thenComparing()fungsi untuk secara efektif menggabungkan dua perbandingan menjadi satu:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted             
       .thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);

Kode di atas akan mengurutkan daftar berdasarkan timeStarted, dan kemudian oleh timeEnded(untuk catatan yang memiliki yang sama timeStarted).

Satu catatan terakhir: Sangat mudah untuk membandingkan primitif 'panjang' atau 'int', Anda dapat mengurangi satu dari yang lainnya. Jika Anda membandingkan objek ('Panjang' atau 'Tali'), saya sarankan Anda menggunakan perbandingan bawaannya. Contoh:

Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );

EDIT: Terima kasih kepada Lukas Eder karena menunjuk saya .thenComparing()berfungsi.

John Fowler
sumber
4
Anda mungkin menghargai Java 8 API baru Comparator.comparing().thenComparing()...
Lukas Eder
1
Hati-hati dengan perbedaan pengembalian. Jika terjadi luapan, Anda bisa mendapatkan hasil yang salah.
krzychu
2
Ini adalah IMHO metode penyortiran yang lebih mudah, sekali lagi dengan asumsi Anda tidak memiliki urutan alami yang jelas. Anda juga tidak perlu menelepon Collectionslagi, Anda dapat memanggil langsung ke daftar. Misalnya:myList.sort(Comparator.comparing(Address::getZipCode).thenComparing(Compartor.comparing(Address::getStreetName));
CeePlusPlus
20

Kami dapat mengurutkan daftar dalam satu dari dua cara:

1. Menggunakan Komparator : Ketika diminta untuk menggunakan logika pengurutan di banyak tempat. Jika Anda ingin menggunakan logika pengurutan di satu tempat, maka Anda dapat menulis kelas dalam anonim sebagai berikut, atau mengekstrak komparator dan menggunakannya di banyak tempat

  Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
        public int compare(ActiveAlarm o1, ActiveAlarm o2) {
            //Sorts by 'TimeStarted' property
            return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
        }

        //If 'TimeStarted' property is equal sorts by 'TimeEnded' property
        public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
            return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
        }
    });

Kita dapat memiliki pemeriksaan nol untuk properti, jika kita bisa menggunakan 'Panjang' dan bukan 'panjang'.

2. Menggunakan Comparable (pengurutan alami) : Jika algoritma sortir selalu menempel pada satu properti: tulis kelas yang mengimplementasikan metode 'Sebanding' dan ganti metode 'compareTo' seperti yang didefinisikan di bawah ini

class ActiveAlarm implements Comparable<ActiveAlarm>{

public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;

public ActiveAlarm(long timeStarted,long timeEnded) {
    this.timeStarted=timeStarted;
    this.timeEnded=timeEnded;
}

public long getTimeStarted() {
    return timeStarted;
}

public long getTimeEnded() {
    return timeEnded;
}

public int compareTo(ActiveAlarm o) {
    return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}

public int doSecodaryOrderSort(ActiveAlarm o) {
    return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}

}

panggil metode sortir untuk mengurutkan berdasarkan pemesanan alami

Collections.sort(list);
Jagadeesh
sumber
7

Dalam java8 + ini dapat ditulis dalam satu baris sebagai berikut:

collectionObjec.sort(comparator_lamda) atau comparator.comparing(CollectionType::getterOfProperty)

kode:

ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
 

atau

ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
Karthikeyan
sumber
5
public class ActiveAlarm implements Comparable<ActiveAlarm> {
    public long timeStarted;
    public long timeEnded;
    private String name = "";
    private String description = "";
    private String event;
    private boolean live = false;

    public int compareTo(ActiveAlarm a) {
        if ( this.timeStarted > a.timeStarted )
            return 1;
        else if ( this.timeStarted < a.timeStarted )
            return -1;
        else {
             if ( this.timeEnded > a.timeEnded )
                 return 1;
             else
                 return -1;
        }
 }

Itu seharusnya memberi Anda ide kasar. Setelah selesai, Anda dapat memanggil Collections.sort()daftar.

Kal
sumber
4

Sejak Java8 ini dapat dilakukan lebih bersih menggunakan kombinasi ComparatordanLambda expressions

Sebagai contoh:

class Student{

    private String name;
    private List<Score> scores;

    // +accessor methods
}

class Score {

    private int grade;
    // +accessor methods
}

    Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);
Mathias G.
sumber
2

Rantai Perbandingan Guava :

Collections.sort(list, new Comparator<ActiveAlarm>(){
            @Override
            public int compare(ActiveAlarm a1, ActiveAlarm a2) {
                 return ComparisonChain.start()
                       .compare(a1.timestarted, a2.timestarted)
                       //...
                       .compare(a1.timeEnded, a1.timeEnded).result();
            }});
卢 声 远 Shengyuan Lu
sumber
1

Di java Anda perlu menggunakan Collections.sortmetode statis . Berikut ini adalah contoh untuk daftar objek CompanyRole, disortir pertama dengan mulai dan kemudian pada akhir. Anda dapat dengan mudah beradaptasi untuk objek Anda sendiri.

private static void order(List<TextComponent> roles) {

    Collections.sort(roles, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            int x1 = ((CompanyRole) o1).getBegin();
            int x2 = ((CompanyRole) o2).getBegin();

            if (x1 != x2) {
                return x1 - x2;
            } else {
                int y1 = ((CompanyRole) o1).getEnd();
                int y2 = ((CompanyRole) o2).getEnd();
                return y2 - y1;
            }
        }
    });
}
Richard H
sumber
0

Anda dapat memanggil Collections.sort () dan mengirimkan Komparator yang perlu Anda tulis untuk membandingkan berbagai properti objek.

Liv
sumber
0

Seperti yang disebutkan, Anda dapat mengurutkan berdasarkan:

  • Membuat objek Anda diimplementasikan Comparable
  • Atau berikan ComparatorkepadaCollections.sort

Jika Anda melakukan keduanya, itu Comparableakan diabaikan dan Comparatorakan digunakan. Ini membantu bahwa objek nilai memiliki logika mereka sendiri Comparableyang merupakan pengurutan yang paling masuk akal untuk objek nilai Anda, sementara setiap use case memiliki implementasi sendiri.

Alireza Fattahi
sumber