3つのフィールド(すべて文字列タイプ)を持つ「レポート」オブジェクトのリストがあります。
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());
}
});
何らかの理由で、ソートされた順序がありません。フィールド間にスペースを入れることをお勧めしますが、なぜですか?
コードに何か問題がありますか?
コードに何か問題がありますか?
はい。比較する前に3つのフィールドを一緒に追加するのはなぜですか?
私はおそらくこのようなことをするでしょう:(フィールドがあなたがそれらをソートしたい順番にあると仮定して)
@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;
}
(from Java)のオブジェクトのリストをソートする方法 )
this Gist の作業コード
Java 8はこれをラムダでうまく解決します(ただし、GuavaとApache Commonsはさらに柔軟性を提供します)。
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
@gaoagongの 以下の回答 に感謝します。
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);
}
});
これには多くの入力、メンテナンスが必要で、エラーが発生しやすくなります。
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
明らかにこれはより簡潔ですが、代わりに文字列を使用してフィールドへの直接参照を失うため、さらにエラーが発生しやすくなります(型安全性、自動リファクタリングなし)。これで、フィールドの名前が変更されても、コンパイラは問題を報告しません。さらに、このソリューションはリフレクションを使用するため、ソートがはるかに遅くなります。
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)が必要です。
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();
}
});
GuavaのComparisonChainと同様に、このライブラリクラスは複数のフィールドで簡単に並べ替えられますが、null値(1、a、b、z、nullなど)のデフォルトの動作も定義します。ただし、独自のコンパレータを提供しない限り、他のものも指定できません。
最終的には、フレーバーと柔軟性(GuavaのComparisonChain)と簡潔なコード(ApacheのCompareToBuilder)が必要になります。
優先順位 CodeReviewでMultiComparator
で複数のコンパレーターを組み合わせたNiceソリューションを見つけました。
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)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Guava の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 + '\'' +
'}';
}
}
}
レポートキー、学生番号、学校の順に並べ替える場合は、次のようにする必要があります。
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値を許可する必要がある場合は、より複雑になります。
couldを使用して、スペースを使用して文字列連結バージョンを取得しますが、奇妙な場合、スペースなどを含む奇妙なデータがあると失敗します。上記のコードはlogical =必要なコード...最初にレポートキーで比較し、次にレポートキーが同じ場合にのみ学生番号で悩まします。
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 + "]";
}
}
JDK1.8で導入されたメソッドでComparator
インターフェイスを使用します:comparing
およびthenComparing
、またはより具体的なメソッド:comparingXXX
およびthenComparingXXX
。
たとえば、最初にIDで、次に年齢で、次に名前で人のリストをソートする場合:
Comparator<Person> comparator = Comparator.comparingLong(Person::getId)
.thenComparingInt(Person::getAge)
.thenComparing(Person::getName);
personList.sort(comparator);
ReportKey、Student Number、Schoolの順に並べ替える場合は、連結するのではなく、各文字列を比較する必要があります。各ReportKeyが同じ長さになるように文字列にスペースを埋め込むとメソッドが機能する場合がありますが、実際に努力する価値はありません。代わりに、compareToが0を返し、StudentNumberを試し、次にSchoolを試す場合、単にCompareメソッドを変更してReportKeysを比較します。
StudentNumberが数値の場合、数値ではなく英数字でソートされます。期待しないでください
"2" < "11"
そうなる:
"11" < "2"
上記の多くの回答には、実際に機能していない単一のコンパレーター方式で比較されたフィールドがあります。いくつかの答えがありますが、各フィールドに異なるコンパレータが実装されているため、この例をより明確で理解しやすいと信じているため、これを投稿しています。
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()));
}
}
[〜#〜] output [〜#〜]
降順で並べ替えられた生徒のリストは、[生徒[bornYear = 2018、bornMonth = null、bornDay = null]、生徒[bornYear = 2018、bornMonth = 12、bornDay = null]、生徒[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]]
オブジェクトの2つのフィールド(1つのストリングと1つの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