Ich habe eine Liste von "Berichts" -Objekten mit drei Feldern (Typ "Alle Zeichenfolgen").
ReportKey
StudentNumber
School
Ich habe einen Sortiercode wie folgt:
Collections.sort(reportList, new Comparator<Report>() {
@Override
public int compare(final Report record1, final Report record2) {
return (record1.getReportKey() + record1.getStudentNumber() + record1.getSchool())
.compareTo(record2.getReportKey() + record2.getStudentNumber() + record2.getSchool());
}
});
Aus irgendeinem Grund habe ich die sortierte Reihenfolge nicht. Man riet, Leerzeichen zwischen Felder zu setzen, aber warum?
Sehen Sie etwas falsch mit dem Code?
java
sorting
collections
Milli Szabo
quelle
quelle
Antworten:
Ja. Warum addieren Sie die drei Felder, bevor Sie sie vergleichen?
Ich würde wahrscheinlich so etwas tun: (vorausgesetzt, die Felder sind in der Reihenfolge, in der Sie sie sortieren möchten)
@Override public int compare(final Report record1, final Report record2) { int c; c = record1.getReportKey().compareTo(record2.getReportKey()); if (c == 0) c = record1.getStudentNumber().compareTo(record2.getStudentNumber()); if (c == 0) c = record1.getSchool().compareTo(record2.getSchool()); return c; }
quelle
a.compareTo(b)
. Die Konvention ist, dass ein Wert von 0 Gleichheit darstellt, negative Ganzzahlen dies darstellena < b
und positive Ganzzahlen diesa > b
fürComparable
a
und darstellenb
.compareTo
(ursprünglich von Möglichkeiten zum Sortieren von Objektlisten in Java anhand mehrerer Felder )
Originaler Arbeitscode in diesem Kern
Verwenden von Java 8 Lambda (hinzugefügt am 10. April 2019)
Java 8 löst dies gut durch Lambda (obwohl Guava und Apache Commons möglicherweise noch mehr Flexibilität bieten):
Vielen Dank an @ gaoagongs Antwort unten .
Beachten Sie, dass ein Vorteil hierbei darin besteht, dass die Getter träge bewertet werden (z. B.
getSchool()
wird nur bewertet, wenn dies relevant ist).Chaotisch und verschlungen: Sortieren von Hand
Collections.sort(pizzas, new Comparator<Pizza>() { @Override public int compare(Pizza p1, Pizza p2) { int sizeCmp = p1.size.compareTo(p2.size); if (sizeCmp != 0) { return sizeCmp; } int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings); if (nrOfToppingsCmp != 0) { return nrOfToppingsCmp; } return p1.name.compareTo(p2.name); } });
Dies erfordert viel Eingabe, Wartung und ist fehleranfällig. Der einzige Vorteil ist, dass Getter nur dann aufgerufen werden, wenn dies relevant ist.
Der reflektierende Weg: Sortieren mit BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList( new BeanComparator("size"), new BeanComparator("nrOfToppings"), new BeanComparator("name"))); Collections.sort(pizzas, chain);
Dies ist natürlich prägnanter, aber noch fehleranfälliger, da Sie Ihren direkten Verweis auf die Felder verlieren, indem Sie stattdessen Strings verwenden (keine Typensicherheit, automatische Refactorings). Wenn nun ein Feld umbenannt wird, meldet der Compiler nicht einmal ein Problem. Da diese Lösung Reflexion verwendet, ist die Sortierung außerdem viel langsamer.
Anreise: Sortieren mit Google Guavas ComparisonChain
Collections.sort(pizzas, new Comparator<Pizza>() { @Override public int compare(Pizza p1, Pizza p2) { return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result(); // or in case the fields can be null: /* return ComparisonChain.start() .compare(p1.size, p2.size, Ordering.natural().nullsLast()) .compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast()) .compare(p1.name, p2.name, Ordering.natural().nullsLast()) .result(); */ } });
Dies ist viel besser, erfordert jedoch einen Kesselplattencode für den häufigsten Anwendungsfall: Nullwerte sollten standardmäßig weniger bewertet werden. Für Nullfelder müssen Sie Guava eine zusätzliche Anweisung geben, was in diesem Fall zu tun ist. Dies ist ein flexibler Mechanismus, wenn Sie etwas Bestimmtes tun möchten, aber häufig den Standardfall (z. B. 1, a, b, z, null).
Und wie in den Kommentaren unten angegeben, werden diese Getter alle sofort für jeden Vergleich bewertet.
Sortieren mit Apache Commons CompareToBuilder
Collections.sort(pizzas, new Comparator<Pizza>() { @Override public int compare(Pizza p1, Pizza p2) { return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison(); } });
Wie Guavas ComparisonChain sortiert diese Bibliotheksklasse problemlos nach mehreren Feldern, definiert jedoch auch das Standardverhalten für Nullwerte (z. B. 1, a, b, z, null). Sie können jedoch auch nichts anderes angeben, es sei denn, Sie stellen Ihren eigenen Komparator bereit.
Wie in den Kommentaren unten angegeben, werden diese Getter alle sofort für jeden Vergleich bewertet.
So
Letztendlich kommt es auf den Geschmack und das Bedürfnis nach Flexibilität (Guavas ComparisonChain) im Vergleich zu prägnantem Code (Apaches CompareToBuilder) an.
Bonusmethode
Ich fand eine schöne Lösung , dass mehrere Komparatoren in der Reihenfolge ihrer Priorität Mähdrescher auf Codereview in einem
MultiComparator
:class MultiComparator<T> implements Comparator<T> { private final List<Comparator<T>> comparators; public MultiComparator(List<Comparator<? super T>> comparators) { this.comparators = comparators; } public MultiComparator(Comparator<? super T>... comparators) { this(Arrays.asList(comparators)); } public int compare(T o1, T o2) { for (Comparator<T> c : comparators) { int result = c.compare(o1, o2); if (result != 0) { return result; } } return 0; } public static <T> void sort(List<T> list, Comparator<? super T>... comparators) { Collections.sort(list, new MultiComparator<T>(comparators)); } }
Natürlich hat Apache Commons Collections bereits einen Nutzen dafür:
ComparatorUtils.chainedComparator (compareatorCollection)
quelle
Ich würde einen Komparator mit machen Guava ‚s
ComparisonChain
:public class ReportComparator implements Comparator<Report> { public int compare(Report r1, Report r2) { return ComparisonChain.start() .compare(r1.getReportKey(), r2.getReportKey()) .compare(r1.getStudentNumber(), r2.getStudentNumber()) .compare(r1.getSchool(), r2.getSchool()) .result(); } }
quelle
Dies ist eine alte Frage, daher sehe ich kein Java 8-Äquivalent. Hier ist ein Beispiel für diesen speziellen Fall.
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Compares multiple parts of the Report object. */ public class SimpleJava8ComparatorClass { public static void main(String[] args) { List<Report> reportList = new ArrayList<>(); reportList.add(new Report("reportKey2", "studentNumber2", "school1")); reportList.add(new Report("reportKey4", "studentNumber4", "school6")); reportList.add(new Report("reportKey1", "studentNumber1", "school1")); reportList.add(new Report("reportKey3", "studentNumber2", "school4")); reportList.add(new Report("reportKey2", "studentNumber2", "school3")); System.out.println("pre-sorting"); System.out.println(reportList); System.out.println(); Collections.sort(reportList, Comparator.comparing(Report::getReportKey) .thenComparing(Report::getStudentNumber) .thenComparing(Report::getSchool)); System.out.println("post-sorting"); System.out.println(reportList); } private static class Report { private String reportKey; private String studentNumber; private String school; public Report(String reportKey, String studentNumber, String school) { this.reportKey = reportKey; this.studentNumber = studentNumber; this.school = school; } public String getReportKey() { return reportKey; } public void setReportKey(String reportKey) { this.reportKey = reportKey; } public String getStudentNumber() { return studentNumber; } public void setStudentNumber(String studentNumber) { this.studentNumber = studentNumber; } public String getSchool() { return school; } public void setSchool(String school) { this.school = school; } @Override public String toString() { return "Report{" + "reportKey='" + reportKey + '\'' + ", studentNumber='" + studentNumber + '\'' + ", school='" + school + '\'' + '}'; } } }
quelle
comparing
undthenComparing
für den Sieg!Wenn Sie nach Berichtsschlüssel, Schülernummer und Schule sortieren möchten, sollten Sie Folgendes tun:
public class ReportComparator implements Comparator<Report> { public int compare(Report r1, Report r2) { int result = r1.getReportKey().compareTo(r2.getReportKey()); if (result != 0) { return result; } result = r1.getStudentNumber().compareTo(r2.getStudentNumber()); if (result != 0) { return result; } return r1.getSchool().compareTo(r2.getSchool()); } }
Dies setzt natürlich voraus, dass keiner der Werte null sein kann - es wird komplizierter, wenn Sie Nullwerte für den Bericht, den Berichtsschlüssel, die Schülernummer oder die Schule zulassen müssen.
Während Sie die Version zur Verkettung von Zeichenfolgen mithilfe von Leerzeichen zum Laufen bringen könnten , würde dies in seltsamen Fällen immer noch fehlschlagen, wenn Sie ungerade Daten hätten, die selbst Leerzeichen usw. enthalten. Der obige Code ist der gewünschte logische Code ... Vergleichen Sie ihn zuerst mit dem Berichtsschlüssel sich nur mit der Schülernummer beschäftigen, wenn die Berichtsschlüssel identisch sind usw.
quelle
compareTo()
Methode verwenden. Könnten Sie mir bitte helfen, das Problem zu lösen?Comparable<T>
, implementiere ichComparator<T>
. Wir wissen nicht, was Sie erreichen wollen, was Sie versucht haben oder was schief gelaufen ist. Vielleicht sollten Sie eine neue Frage stellen, wahrscheinlich nachdem Sie mehr recherchiert haben. (Möglicherweise wurde es bereits gefragt.)Ich schlage vor, den Java 8 Lambda-Ansatz zu verwenden:
List<Report> reportList = new ArrayList<Report>(); reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
quelle
Sortieren mit mehreren Feldern in Java8
package com.java8.chapter1; import java.util.Arrays; import java.util.Comparator; import java.util.List; import static java.util.Comparator.*; public class Example1 { public static void main(String[] args) { List<Employee> empList = getEmpList(); // Before Java 8 empList.sort(new Comparator<Employee>() { @Override public int compare(Employee o1, Employee o2) { int res = o1.getDesignation().compareTo(o2.getDesignation()); if (res == 0) { return o1.getSalary() > o2.getSalary() ? 1 : o1.getSalary() < o2.getSalary() ? -1 : 0; } else { return res; } } }); for (Employee emp : empList) { System.out.println(emp); } System.out.println("---------------------------------------------------------------------------"); // In Java 8 empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary)); empList.stream().forEach(System.out::println); } private static List<Employee> getEmpList() { return Arrays.asList(new Employee("Lakshman A", "Consultent", 450000), new Employee("Chaitra S", "Developer", 250000), new Employee("Manoj PVN", "Developer", 250000), new Employee("Ramesh R", "Developer", 280000), new Employee("Suresh S", "Developer", 270000), new Employee("Jaishree", "Opearations HR", 350000)); } } class Employee { private String fullName; private String designation; private double salary; public Employee(String fullName, String designation, double salary) { super(); this.fullName = fullName; this.designation = designation; this.salary = salary; } public String getFullName() { return fullName; } public String getDesignation() { return designation; } public double getSalary() { return salary; } @Override public String toString() { return "Employee [fullName=" + fullName + ", designation=" + designation + ", salary=" + salary + "]"; } }
quelle
empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
Dieser Code hat mir geholfen. Vielen DankWenn die StudentNumber numerisch ist, wird sie nicht numerisch, sondern alphanumerisch sortiert. Erwarte nicht
"2" < "11"
es wird sein:
"11" < "2"
quelle
Wenn Sie zuerst nach ReportKey, dann nach Schülernummer und dann nach Schule sortieren möchten, müssen Sie jeden String vergleichen, anstatt ihn zu verketten. Ihre Methode funktioniert möglicherweise, wenn Sie die Zeichenfolgen mit Leerzeichen auffüllen, sodass jeder ReportKey dieselbe Länge usw. hat, aber die Mühe lohnt sich nicht wirklich. Ändern Sie stattdessen einfach die Vergleichsmethode, um die ReportKeys zu vergleichen. Wenn compareTo 0 zurückgibt, versuchen Sie es mit StudentNumber und dann mit School.
quelle
Verwenden Sie die
Comparator
Schnittstelle mit den in JDK1.8 eingeführten Methoden:comparing
undthenComparing
oder konkreteren Methoden:comparingXXX
undthenComparingXXX
.Wenn wir zum Beispiel zuerst eine Liste von Personen nach ihrer ID sortieren möchten, dann Alter, dann Name:
quelle
Hier ist ein vollständiges Beispiel für den Vergleich von zwei Feldern in einem Objekt, einem String und einem int, wobei auch Collator zum Sortieren verwendet wird.
public class Test { public static void main(String[] args) { Collator myCollator; myCollator = Collator.getInstance(Locale.US); List<Item> items = new ArrayList<Item>(); items.add(new Item("costrels", 1039737, "")); items.add(new Item("Costs", 1570019, "")); items.add(new Item("costs", 310831, "")); items.add(new Item("costs", 310832, "")); Collections.sort(items, new Comparator<Item>() { @Override public int compare(final Item record1, final Item record2) { int c; //c = record1.item1.compareTo(record2.item1); //optional comparison without Collator c = myCollator.compare(record1.item1, record2.item1); if (c == 0) { return record1.item2 < record2.item2 ? -1 : record1.item2 > record2.item2 ? 1 : 0; } return c; } }); for (Item item : items) { System.out.println(item.item1); System.out.println(item.item2); } } public static class Item { public String item1; public int item2; public String item3; public Item(String item1, int item2, String item3) { this.item1 = item1; this.item2 = item2; this.item3 = item3; } } }
Ausgabe:
costrels 1039737
kostet 310831
kostet 310832
Kosten 1570019
quelle
Viele der oben genannten Antworten enthalten Felder, die mit der Einzelkomparatormethode verglichen werden, die tatsächlich nicht funktioniert. Es gibt einige Antworten, obwohl für jedes Feld unterschiedliche Komparatoren implementiert sind. Ich poste dies, da dieses Beispiel meiner Meinung nach viel klarer und einfacher zu verstehen wäre.
class Student{ Integer bornYear; Integer bornMonth; Integer bornDay; public Student(int bornYear, int bornMonth, int bornDay) { this.bornYear = bornYear; this.bornMonth = bornMonth; this.bornDay = bornDay; } public Student(int bornYear, int bornMonth) { this.bornYear = bornYear; this.bornMonth = bornMonth; } public Student(int bornYear) { this.bornYear = bornYear; } public Integer getBornYear() { return bornYear; } public void setBornYear(int bornYear) { this.bornYear = bornYear; } public Integer getBornMonth() { return bornMonth; } public void setBornMonth(int bornMonth) { this.bornMonth = bornMonth; } public Integer getBornDay() { return bornDay; } public void setBornDay(int bornDay) { this.bornDay = bornDay; } @Override public String toString() { return "Student [bornYear=" + bornYear + ", bornMonth=" + bornMonth + ", bornDay=" + bornDay + "]"; } } class TestClass { // Comparator problem in JAVA for sorting objects based on multiple fields public static void main(String[] args) { int N,c;// Number of threads Student s1=new Student(2018,12); Student s2=new Student(2018,12); Student s3=new Student(2018,11); Student s4=new Student(2017,6); Student s5=new Student(2017,4); Student s6=new Student(2016,8); Student s7=new Student(2018); Student s8=new Student(2017,8); Student s9=new Student(2017,2); Student s10=new Student(2017,9); List<Student> studentList=new ArrayList<>(); studentList.add(s1); studentList.add(s2); studentList.add(s3); studentList.add(s4); studentList.add(s5); studentList.add(s6); studentList.add(s7); studentList.add(s8); studentList.add(s9); studentList.add(s10); Comparator<Student> byMonth=new Comparator<Student>() { @Override public int compare(Student st1,Student st2) { if(st1.getBornMonth()!=null && st2.getBornMonth()!=null) { return st2.getBornMonth()-st1.getBornMonth(); } else if(st1.getBornMonth()!=null) { return 1; } else { return -1; } }}; Collections.sort(studentList, new Comparator<Student>() { @Override public int compare(Student st1,Student st2) { return st2.getBornYear()-st1.getBornYear(); }}.thenComparing(byMonth)); System.out.println("The sorted students list in descending is"+Arrays.deepToString(studentList.toArray())); } }
AUSGABE
Die sortierte Schülerliste in absteigender Reihenfolge lautet [Student [bornYear = 2018, bornMonth = null, bornDay = null], Student [bornYear = 2018, bornMonth = 12, bornDay = null], Student [bornYear = 2018, bornMonth = 12, bornDay = null], Student [bornYear = 2018, bornMonth = 11, bornDay = null], Student [bornYear = 2017, bornMonth = 9, bornDay = null], Student [bornYear = 2017, bornMonth = 8, bornDay = null], Student [ bornYear = 2017, bornMonth = 6, bornDay = null], Student [bornYear = 2017, bornMonth = 4, bornDay = null], Student [bornYear = 2017, bornMonth = 2, bornDay = null], Student [bornYear = 2016, bornMonth = 8, bornDay = null]]
quelle