Mam listę obiektów „Raport” z trzema polami (wszystkie typu String) -
ReportKey
StudentNumber
School
Mam numer sortowania, który wygląda następująco-
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());
}
});
Z jakiegoś powodu nie mam posortowanej kolejności. Zalecano wstawianie spacji między polami, ale dlaczego?
Czy widzisz coś złego w kodzie?
java
sorting
collections
Milli Szabo
źródło
źródło
Odpowiedzi:
Tak. Dlaczego dodajesz trzy pola do siebie, zanim je porównasz?
Prawdopodobnie zrobiłbym coś takiego: (zakładając, że pola są w kolejności, w jakiej chcesz je sortować)
@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; }
źródło
a.compareTo(b)
; Konwencja jest taka, że wartość 0 reprezentuje równość, ujemne liczby całkowite to,a < b
a dodatnie liczby całkowite reprezentują toa > b
dlaComparable
a
ib
.compareTo
(pierwotnie ze sposobów sortowania list obiektów w Javie na podstawie wielu pól )
Oryginalny działający kod w tym istocie
Korzystanie z lambda Java 8 (dodano 10 kwietnia 2019 r.)
Java 8 ładnie rozwiązuje to za pomocą lambda (chociaż Guava i Apache Commons mogą nadal oferować większą elastyczność):
Dzięki poniższej odpowiedzi @ gaoagong .
Zwróć uwagę, że jedną z zalet jest to, że metody pobierające są oceniane leniwie (np. Są oceniane
getSchool()
tylko wtedy, gdy są istotne).Niechlujny i zawiły: sortowanie ręczne
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); } });
Wymaga to dużo pisania, konserwacji i jest podatne na błędy. Jedyną zaletą jest to, że metody pobierające są wywoływane tylko wtedy, gdy są istotne.
Odblaskowy sposób: sortowanie za pomocą BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList( new BeanComparator("size"), new BeanComparator("nrOfToppings"), new BeanComparator("name"))); Collections.sort(pizzas, chain);
Oczywiście jest to bardziej zwięzłe, ale jeszcze bardziej podatne na błędy, ponieważ tracisz bezpośrednie odniesienie do pól, używając zamiast tego ciągów znaków (brak bezpieczeństwa typów, autorefaktoryzacja). Teraz, jeśli nazwa pola zostanie zmieniona, kompilator nawet nie zgłosi problemu. Co więcej, ponieważ to rozwiązanie wykorzystuje odbicie, sortowanie przebiega znacznie wolniej.
Jak się tam dostać: sortowanie za pomocą porównywarki Google Guava
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(); */ } });
Jest to o wiele lepsze, ale wymaga trochę kodu kotłowego dla najczęstszego przypadku użycia: wartości null powinny być domyślnie wyceniane mniej. W przypadku pól o wartości null musisz przekazać Guava dodatkową dyrektywę, co robić w takim przypadku. Jest to elastyczny mechanizm, jeśli chcesz zrobić coś konkretnego, ale często potrzebujesz domyślnej wielkości liter (np. 1, a, b, z, null).
Jak zauważono w komentarzach poniżej, wszystkie te metody pobierające są oceniane natychmiast dla każdego porównania.
Sortowanie za pomocą 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(); } });
Podobnie jak CompareChain firmy Guava, ta klasa biblioteki łatwo sortuje na wielu polach, ale także definiuje domyślne zachowanie dla wartości null (np. 1, a, b, z, null). Jednak nie możesz też określić niczego innego, chyba że zapewnisz własny komparator.
Ponownie, jak zauważono w komentarzach poniżej, wszystkie te metody pobierające są oceniane natychmiast dla każdego porównania.
A zatem
Ostatecznie sprowadza się to do smaku i potrzeby elastyczności (Guava's CompareChain) w porównaniu do zwięzłego kodu (PorównajToBuilder Apache).
Metoda bonusowa
Znalazłem fajne rozwiązanie, które łączy wiele komparatorów w kolejności priorytetów w CodeReview w
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)); } }
Oczywiście Apache Commons Collections ma już do tego zastosowanie:
ComparatorUtils.chainedComparator (ComparatorCollection)
źródło
Chciałbym zrobić komparator korzystając 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(); } }
źródło
To jest stare pytanie, więc nie widzę odpowiednika Java 8. Oto przykład dla tego konkretnego przypadku.
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 + '\'' + '}'; } } }
źródło
comparing
ithenComparing
po zwycięstwo!Jeśli chcesz posortować według klucza raportu, następnie numeru ucznia, a następnie szkoły, powinieneś zrobić coś takiego:
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()); } }
Zakłada to oczywiście, że żadna z wartości nie może być zerowa - sytuacja staje się bardziej skomplikowana, jeśli trzeba zezwolić na wartości zerowe dla raportu, klucza raportu, numeru ucznia lub szkoły.
Podczas mogli uzyskać wersję ciąg konkatenacji do pracy za pomocą spacji, to jeszcze nie w dziwnych przypadków, jeśli miał dane nieparzyste która sama łącznie ze spacjami itp Powyższy kod jest logiczne kod chcesz ... porównanie kluczem raportu, potem zawracaj sobie głowę numerem studenta tylko wtedy, gdy klucze raportu są takie same itp.
źródło
compareTo()
metody. Czy mógłbyś mi pomóc rozwiązać problem.Comparable<T>
, jestem wykonawczychComparator<T>
. Nie wiemy, co próbujesz osiągnąć, czego próbowałeś lub co poszło nie tak. Być może powinieneś zadać nowe pytanie, prawdopodobnie po przeprowadzeniu dalszych badań. (Być może już o to pytano.)Proponuję zastosować podejście Java 8 Lambda:
List<Report> reportList = new ArrayList<Report>(); reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
źródło
Sortowanie z wieloma polami w Javie8
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 + "]"; } }
źródło
empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
ten kod mi pomógł. DziękiJeśli numer ucznia jest numeryczny, nie będzie sortowany numerycznie, ale alfanumerycznie. Nie oczekuj
"2" < "11"
To będzie:
"11" < "2"
źródło
Jeśli chcesz posortować najpierw na podstawie ReportKey, a następnie numeru ucznia, a następnie szkoły, musisz porównać każdy ciąg znaków, zamiast je łączyć. Twoja metoda może zadziałać, jeśli dopełnisz łańcuchy spacjami, tak aby każdy klucz ReportKey był tej samej długości i tak dalej, ale nie jest to naprawdę warte wysiłku. Zamiast tego po prostu zmień metodę porównania, aby porównać ReportKeys, jeśli compareTo zwraca 0, a następnie wypróbuj StudentNumber, a następnie School.
źródło
Użyj
Comparator
interfejsu z metodami wprowadzonymi w JDK1.8:comparing
ithenComparing
, lub bardziej konkretnymi metodami:comparingXXX
ithenComparingXXX
.Na przykład, jeśli chcemy posortować listę osób najpierw według ich identyfikatora, następnie wieku, a następnie imienia:
źródło
Oto pełny przykład porównania 2 pól w obiekcie, jednego ciągu znaków i jednego elementu int, również przy użyciu narzędzia Collator do sortowania.
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; } } }
Wynik:
Wiśniewska 1039737
kosztuje 310831
kosztuje 310832
Koszty 1570019
źródło
Wiele powyższych odpowiedzi ma pola porównane w jednej metodzie porównawczej, która w rzeczywistości nie działa. Jest kilka odpowiedzi, chociaż z różnymi komparatorami zaimplementowanymi dla każdego pola, zamieszczam to, ponieważ ten przykład byłby znacznie jaśniejszy i łatwiejszy do zrozumienia, w które wierzę.
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())); } }
WYNIK
Posortowana lista uczniów w kolejności malejącej to [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]]
źródło