У меня есть список объектов «Отчет» с тремя полями (тип All String) -
ReportKey
StudentNumber
School
У меня есть код сортировки:
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());
}
});
По какой-то причине у меня нет отсортированного порядка. Один советовал ставить пробелы между полями, но почему?
Вы видите что-то не так с кодом?
java
sorting
collections
Милли Сабо
источник
источник
Ответы:
Да. Почему вы складываете три поля вместе, прежде чем сравнивать их?
Я бы, вероятно, сделал что-то вроде этого: (при условии, что поля находятся в том порядке, в котором вы хотите их отсортировать)
@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; }
источник
a.compareTo(b)
; соглашение состоит в том, что значение 0 представляет равенство, отрицательные целые числа представляют это,a < b
а положительные целые числа представляют этоa > b
дляComparable
a
иb
.compareTo
(изначально из Ways для сортировки списков объектов в Java на основе нескольких полей )
Исходный рабочий код в этом смысле
Использование лямбда-выражений Java 8 (добавлено 10 апреля 2019 г.)
Java 8 прекрасно решает эту проблему с помощью лямбды (хотя Guava и Apache Commons могут по-прежнему предлагать большую гибкость):
Благодаря ответу @ gaoagong ниже .
Обратите внимание, что одним из преимуществ здесь является то, что геттеры вычисляются лениво (например,
getSchool()
оцениваются только в случае необходимости).Беспорядочно и запутанно: сортировка вручную
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); } });
Это требует много ввода, обслуживания и подвержено ошибкам. Единственное преимущество в том, что геттеры вызываются только при необходимости.
Рефлексивный способ: сортировка с помощью BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList( new BeanComparator("size"), new BeanComparator("nrOfToppings"), new BeanComparator("name"))); Collections.sort(pizzas, chain);
Очевидно, это более лаконично, но еще больше подвержено ошибкам, поскольку вы теряете прямую ссылку на поля, используя вместо этого строки (без обеспечения безопасности типов, авторефакторинга). Теперь, если поле будет переименовано, компилятор даже не сообщит о проблеме. Более того, поскольку это решение использует отражение, сортировка выполняется намного медленнее.
Как добраться: сортировка с помощью ComparisonChain 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(); */ } });
Это намного лучше, но для наиболее распространенного варианта использования требуется некоторый шаблонный код: по умолчанию нулевые значения должны иметь меньшее значение. Для нулевых полей вы должны предоставить Guava дополнительную директиву, что делать в этом случае. Это гибкий механизм, если вы хотите сделать что-то конкретное, но часто вам нужен регистр по умолчанию (например, 1, a, b, z, null).
И, как отмечено в комментариях ниже, все эти геттеры сразу оцениваются для каждого сравнения.
Сортировка с помощью 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(); } });
Как и ComparisonChain Guava, этот библиотечный класс легко сортирует по нескольким полям, но также определяет поведение по умолчанию для нулевых значений (например, 1, a, b, z, null). Однако вы также не можете указать что-либо еще, если не предоставите свой собственный Comparator.
Опять же, как отмечено в комментариях ниже, все эти геттеры немедленно оцениваются для каждого сравнения.
Таким образом
В конечном итоге все сводится к вкусу и необходимости гибкости (ComparisonChain Guava) по сравнению с кратким кодом (CompareToBuilder от Apache).
Бонусный метод
Я нашел хорошее решение, которое объединяет несколько компараторов в порядке приоритета на CodeReview в
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)); } }
Конечно, у Apache Commons Collections уже есть утилита для этого:
ComparatorUtils.chainedComparator (comparatorCollection)
источник
Я бы сделать компаратор с помощью гуавы «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(); } }
источник
Это старый вопрос, поэтому я не вижу эквивалента для Java 8. Вот пример для этого конкретного случая.
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 + '\'' + '}'; } } }
источник
comparing
иthenComparing
на победу!Если вы хотите отсортировать по ключу отчета, затем по номеру учащегося, а затем по школе, вы должны сделать что-то вроде этого:
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()); } }
Это, конечно, предполагает, что ни одно из значений не может быть нулевым - это усложняется, если вам нужно разрешить нулевые значения для отчета, ключа отчета, номера ученика или школы.
Хотя вы можете заставить версию конкатенации строк работать с использованием пробелов, она все равно не сработает в странных случаях, если у вас были нечетные данные, которые сами включали пробелы и т. Д. Приведенный выше код - это логический код, который вы хотите ... сначала сравните по ключу отчета, затем беспокоиться о номере ученика, только если ключи отчета совпадают и т. д.
источник
compareTo()
метод. Не могли бы вы помочь мне решить проблему.Comparable<T>
, я реализуюComparator<T>
. Мы не знаем, чего вы пытаетесь достичь, что вы пробовали или что пошло не так. Возможно, вам следует задать новый вопрос, возможно, после дополнительных исследований. (Возможно, об этом уже спрашивали.)Я предлагаю использовать подход Java 8 Lambda:
List<Report> reportList = new ArrayList<Report>(); reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
источник
Сортировка с несколькими полями в 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 + "]"; } }
источник
empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
этот код мне помог. СпасибоЕсли StudentNumber числовой, он будет сортироваться не по цифрам, а по буквенно-цифровым. Не ждите
"2" < "11"
это будет:
"11" < "2"
источник
Если вы хотите отсортировать сначала по ReportKey, затем по номеру ученика, а затем по школе, вам нужно сравнить каждую строку, а не объединять их. Ваш метод может работать, если вы дополните строки пробелами, чтобы все ReportKey имел одинаковую длину и т. Д., Но на самом деле это не стоит усилий. Вместо этого просто измените метод compare, чтобы сравнить ReportKeys, если compareTo возвращает 0, попробуйте StudentNumber, затем School.
источник
Используйте
Comparator
интерфейс с методами, представленными в JDK1.8:comparing
иthenComparing
, или более конкретными методами:comparingXXX
иthenComparingXXX
.Например, если мы хотим отсортировать список людей сначала по идентификатору, затем по возрасту, а затем по имени:
источник
Вот полный пример сравнения двух полей в объекте, одного String и одного int, также с использованием Collator для сортировки.
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; } } }
Вывод:
Costrels 1039737
стоит 310831
стоит 310832
Стоимость 1570019
источник
Во многих приведенных выше ответах поля сравниваются в методе одного компаратора, который на самом деле не работает. Есть несколько ответов, хотя с разными компараторами, реализованными для каждого поля, я публикую это, потому что этот пример был бы намного более ясным и простым для понимания, я верю.
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())); } }
ВЫВОД
Отсортированный список студентов в порядке убывания: [Student [bornYear = 2018 ,bornMonth = null ,bornDay = null], Student [bornYear = 2018 ,bornMonth = 12 ,bornDay = null], Student [bornYear = 2018 ,bornMonth = 12 ,bornDay = null], Студент [bornYear = 2018 ,bornMonth = 11 ,bornDay = null], Студент [bornYear = 2017 ,bornMonth = 9 ,bornDay = null], Студент [bornYear = 2017 ,bornMonth = 8 ,bornDay = null], Студент [ bornYear = 2017 ,bornMonth = 6 ,bornDay = null], Студент [bornYear = 2017 ,bornMonth = 4 ,bornDay = null], Студент [bornYear = 2017 ,bornMonth = 2 ,bornDay = null], Студент [bornYear = 2016 ,bornMonth = 8 ,bornDay = null]]
источник