Коллекции.сортировка с несколькими полями
у меня есть список объектов "отчет" с тремя полями (все строковые) -
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());
}
});
по какой-то причине у меня нет отсортированного порядка. Один советовал поставить пробелы между полями, но почему?
ты видишь что-нибудь не так с кодом?
10 ответов:
ты видишь что-нибудь не так с кодом?
да. Почему вы добавляете три поля вместе, прежде чем сравнивать их?
Я бы, вероятно, сделал что-то вроде этого: (предполагая, что поля находятся в том порядке, в котором вы хотите их сортировать)
@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; }
Я бы сделал компаратор с помощью гуавы ' 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 на основе нескольких полей)
рабочий код в этом суть
грязный и запутанный: сортировка вручную
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);очевидно, что это более лаконично, но еще более подвержено ошибкам, поскольку вы теряете прямую ссылку на полей вместо использования строк (отсутствие типизации, автоматического рефакторинга). Теперь, если поле переименовано, компилятор даже не сообщит о проблеме. Кроме того, поскольку это решение использует отражение, сортировка намного медленнее.
получение там: сортировка с помощью Google Guava 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(); */ } });это намного лучше, но требует некоторого кода котельной плиты для наиболее распространенного случая использования: нулевые значения должны быть оценены меньше по умолчанию. Для null-полей, вы должны предоставить дополнительную директива гуаве, что делать в таком случае. Это гибкий механизм, если вы хотите сделать что-то конкретное, но часто вы хотите, чтобы по умолчанию (т. е. 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). Однако вы также не можете указать ничего другого, если не предоставите свой собственный Компаратор.
в конечном счете это сводится к вкусу и необходимости гибкости (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 имеет util для этого уже:
ComparatorUtils.chainedComparator (comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Если вы хотите сортировать по ключу отчета, то номер студента, то школа, вы должны сделать что-то вроде этого:
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()); } }это предполагает, что ни одно из значений не может быть null, конечно - это становится более сложным, если вам нужно разрешить значения null для отчета, ключа отчета, номера студента или школы.
а вы может получите версию конкатенации строк для работы с использованием пробелов, она все равно не будет работать в странных случаях, если у вас есть нечетные данные, которые сами по себе пробелов и т. д. Приведенный выше код является логическое код, который вы хотите... сначала сравните по ключу отчета, а затем только потрудитесь с номером студента, если ключи отчета одинаковы и т. д.
Это старый вопрос, поэтому я не вижу эквивалента 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 + '\'' + '}'; } } }
сортировка с несколькими полями в 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 + "]"; } }
Если вы хотите сортировать на основе ReportKey сначала номер студента, а затем школа, вам нужно сравнить каждую строку вместо их объединения. Ваш метод может работать, если вы заполняете строки пробелами, чтобы каждый ключ отчета был одинаковой длины и т. д., Но это не стоит усилий. Вместо этого просто измените метод сравнения, чтобы сравнить ReportKeys, если compareTo возвращает 0, то попробуйте StudentNumber, а затем School.
Если StudentNumber является числовым, он будет отсортирован не числовым, а буквенно-цифровым. Не ожидайте
"2" < "11"будет:
"11" < "2"
Я предлагаю использовать подход Java 8 Lambda:
List<Report> reportList = new ArrayList<Report>(); reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
вот полный пример сравнения 2 полей в объекте, одна строка и один 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
Comments