J'ai une liste d'objets "Report" avec trois champs (type All String) -
ReportKey
StudentNumber
School
J'ai un code de tri va comme-
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());
}
});
Pour une raison quelconque, je n'ai pas l'ordre trié. On a conseillé de mettre des espaces entre les champs, mais pourquoi?
Voyez-vous quelque chose qui ne va pas avec le code?
Voyez-vous quelque chose qui ne va pas avec le code?
Oui. Pourquoi additionnez-vous les trois champs avant de les comparer?
Je ferais probablement quelque chose comme ceci: (en supposant que les champs sont dans l'ordre dans lequel vous souhaitez les trier)
@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;
}
Je ferais un comparateur en utilisant 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();
}
}
(from Façons de trier des listes d'objets en Java en fonction de plusieurs champs )
Code de travail dans this Gist
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);
}
});
Cela nécessite beaucoup de frappe, de maintenance et est sujet aux erreurs.
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Évidemment, ceci est plus concis, mais encore plus sujet aux erreurs car vous perdez votre référence directe aux champs en utilisant plutôt des chaînes (pas de sécurité de type, de refactorisation automatique). Désormais, si un champ est renommé, le compilateur ne signalera même pas un problème. De plus, comme cette solution utilise la réflexion, le tri est beaucoup plus lent.
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();
*/
}
});
C'est beaucoup mieux, mais nécessite le code de plaque de chaudière pour le cas d'utilisation le plus courant: les valeurs nulles doivent être évaluées moins par défaut. Pour les champs nuls, vous devez fournir une directive supplémentaire à Guava pour savoir quoi faire dans ce cas. C'est un mécanisme flexible si vous voulez faire quelque chose de spécifique, mais vous voulez souvent le cas par défaut (c'est-à-dire 1, a, b, z, null).
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();
}
});
Comme pour ComparisonChain de Guava, cette classe de bibliothèque trie facilement plusieurs champs, mais définit également le comportement par défaut des valeurs NULL (c.-à-d. 1, a, b, z, null). Cependant, vous ne pouvez rien spécifier d’autre, à moins que vous ne fournissiez votre propre comparateur.
En fin de compte, il en va de la saveur et du besoin de flexibilité (Guava’s ComparisonChain) par rapport à un code concis (Apache’s CompareToBuilder).
J'ai trouvé une solution de Nice qui combine plusieurs comparateurs par ordre de priorité sur CodeReview dans une 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));
}
}
Bien sûr, Apache Commons Collections a déjà une utilité:
ComparatorUtils.chainedComparator (comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
C'est une vieille question, je ne vois donc pas d'équivalent Java 8. Voici un exemple pour ce cas particulier.
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 + '\'' +
'}';
}
}
}
Si vous voulez trier par clé de rapport, puis numéro d'étudiant, puis école, vous devriez faire quelque chose comme ceci:
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());
}
}
Bien entendu, cela suppose qu'aucune des valeurs ne peut être nulle. Cela devient plus compliqué si vous devez autoriser les valeurs nulles pour le rapport, la clé de rapport, le numéro d'élève ou l'école.
Bien que vous puissiez obtenir que la version de concaténation de chaînes fonctionne avec des espaces, elle échouerait quand même dans des cas étranges si vous aviez des données impaires qui incluaient des espaces, etc. Le code ci-dessus est le code logical que vous voulez .. comparez d’abord par clé de rapport, puis ne vous embêtez pas avec le numéro d’étudiant si les clés de rapport sont les mêmes, etc.
Tri avec plusieurs champs en 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 + "]";
}
}
Je suggère d'utiliser l'approche Java 8 Lambda:
List<Report> reportList = new ArrayList<Report>();
reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
Utilisez l'interface Comparator
avec les méthodes introduites dans JDK1.8: comparing
et thenComparing
, ou des méthodes plus concrètes: comparingXXX
et thenComparingXXX
.
Par exemple, si nous voulons d'abord trier une liste de personnes par leur identifiant, alors leur âge, leur nom:
Comparator<Person> comparator = Comparator.comparingLong(Person::getId)
.thenComparingInt(Person::getAge)
.thenComparing(Person::getName);
personList.sort(comparator);
Si vous souhaitez effectuer un tri basé sur ReportKey, puis sur Numéro d'étudiant, puis sur Ecole, vous devez comparer chaque chaîne au lieu de les concaténer. Votre méthode fonctionnera peut-être si vous insérez des espaces dans les chaînes afin que chaque rapportKey ait la même longueur, etc., mais cela ne vaut pas la peine. Au lieu de cela, changez simplement la méthode de comparaison pour comparer les ReportKeys. Si compareTo renvoie 0, essayez StudentNumber, puis School.
Si le numéro d'étudiant est numérique, il ne sera pas trié numérique mais alphanumérique.
"2" < "11"
ce sera:
"11" < "2"
Beaucoup de réponses ci-dessus ont comparé les champs dans la méthode du comparateur unique qui ne fonctionne pas réellement. Il existe quelques réponses mais avec différents comparateurs mis en œuvre pour chaque domaine, je publie ceci car cet exemple serait beaucoup plus clair et simple à comprendre, je le crois.
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()));
}
}
SORTIE
La liste des élèves triés par ordre décroissant est [Étudiant [bornYear = 2018, bornMonth = null, bornDay = null], Étudiant [bornYear = 2018, bornMonth = 12, bornDay = null], Étudiant [bornYear = 2018, bornMonth = 12, bornDay = null], Étudiant [bornYear = 2018, bornMonth = 11, bornDay = null], Étudiant [bornYear = 2017, bornMonth = 9, bornDay = null], Étudiant [bornYear = 2017, bornMonth = 8, bornDay = null], Étudiant [ bornYear = 2017, bornMonth = 6, bornDay = null], étudiant [bornYear = 2017, bornMonth = 4, bornDay = null], étudiant [bornYear = 2017, bornMonth = 2, bornDay = null], étudiant [bornYear = 2016, bornMonth = 8, bornDay = null]]
Voici un exemple complet comparant 2 champs dans un objet, un String et un int, utilisant également Collator pour trier.
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;
}
}
}
Sortie:
costrels 1039737
coûts 310831
coûts 310832
Coûts 1570019