私のエンティティクラスは次のようになります。
public class Student {
private int grade;
// other fields and methods
}
そして私はそれをそのように使用します:
List<Student> students = ...;
プライベートフィールドであることを考慮して、students
をgrade
で並べ替えるにはどうすればよいですか?
次のオプションがあります。
grade
を表示するgrade
のゲッターメソッドを定義しますComparator
insideStudent
を定義しますStudent
にComparable
を実装させる解決策の例3
:
public class Student {
private int grade;
public static Comparator<Student> byGrade = Comparator.comparing(s -> s.grade);
}
次のように使用します。
List<Student> students = Arrays.asList(student2, student3, student1);
students.sort(Student.byGrade);
System.out.println(students);
これは私のお気に入りのソリューションです。理由は次のとおりです。
Comparator
sを簡単に定義できます解決策の例4
:
public class Student implements Comparable {
private int grade;
@Override
public int compareTo(Object other) {
if (other instanceof Student) {
return Integer.compare(this.grade, ((Student) other).grade);
}
return -1;
}
}
次のようにどこでも並べ替えることができます:
List<Student> students = Arrays.asList(student2, student3, student1);
Collections.sort(students);
System.out.println(students);
このソリューションの側面:
grade
による並べ替えが学生の自然な順序を表すことを定義しますTreeMap
など)一般に、学生の成績に依存する動作が必要な場合は、この情報にアクセスできる必要があります。他のコードがアクセスできるようにするメソッドまたはプロパティを追加します。
したがって、最も簡単な修正は次のようになります。
public class Student implements IStudent {
...
private int grade;
...
// other fields and methods
public int getGrade() {
return grade;
}
}
おそらくインターフェースIStudent
も拡張する必要があります:)
ただし、これが並べ替えにのみ必要な場合は、他の回答ですでに提案されているアイデアを使用できます。実装 Comparable
インターフェイス。このようにして、grade
を非表示にして、int compareTo
メソッド内で使用できます。
JDK 1.8が提供するオプションは、stream
ライブラリsorted()
methodを使用することで、Comparable
インターフェースを実装する必要はありません。フィールドgrade
にアクセサ(ゲッター)メソッドを実装する必要があります
_public class Student {
private int grade;
public int getGrade() {
return grade;
}
public Student setGrade(int grade) {
this.grade = grade;
return this;
}}
_
次に、unsortedStudentListがあるとすると、次のコードのように並べ替えることができます。
_List<Student> sortedStudentList = unsortedStudentList
.stream()
.sorted(Comparator.comparing(Student::getGrade))
.collect(Collectors.toList());
_
また、sorted()
メソッドを使用すると、他のフィールド(ある場合)に基づいて学生を並べ替えることもできます。たとえば、studentのフィールドname
について考えてみます。この場合、studentListを成績と名前の両方に基づいて並べ替えます。したがって、Student
クラスは次のようになります。
_public class Student {
private int grade;
private String name;
public int getGrade() {
return grade;
}
public Student setGrade(int grade) {
this.grade = grade;
return this;
}
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}}
_
両方のフィールドに基づいて並べ替えるには:
_ List<Student> sortedStudentList = unsortedStudentList
.stream()
.sorted(Comparator.comparing(Student::getGrade)
.thenComparing(Comparator.comparing(Student::getName)))
.collect(Collectors.toList());
_
2番目のコンパレータは、最初のコンパレータが2つの等しいオブジェクトを比較しているときに機能します。
Studentクラスの Comparable interface を実装し、メソッドint compareTo(T o)
を実装します。このようにして、グレードプロパティをプライベートに保つことができます。
クラスはComparable
インターフェースを実装できます。次に、リストを簡単に並べ替えることができます。
public class Student implements IStudent, Comparable<Student>
{
...
private int grade;
...
@Override
public int compareTo(Student other)
{
return (grade - other.grade);
}
}
public class Section
{
private List<IStudent> studentsList;
...
public void sortStudents()
{
studentsList.sort(null);
}
}
本当にアクセスできないフィールドで並べ替える必要がある場合は、reflectionを使用できます。
private static int extractGrade(Student student) {
try {
Field field = Student.class.getDeclaredField("grade");
field.setAccessible(true);
return field.getInt(student);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
Comparator<Student> studentComparator = Comparator.comparingInt(DemoApplication::extractGrade);
List<Student> students = Arrays.asList(new Student(1), new Student(10), new Student(5));
students.sort(studentComparator);
}
しかし、私はこの方法はちょっと危険だと言わなければなりません。
どうしても必要な場合以外は使用しないでください。たとえば、getterメソッドを使用して特定のフィールドへのアクセスを許可することをお勧めします。
また、このコードをモジュールパスでJava 9+に対して実行すると、問題が発生する可能性があります(InaccessibleObjectException
がスローされる可能性があります)。
Comparable
の実装について比較可能から docs :
このインターフェースは、それを実装する各クラスのオブジェクトに全順序を課します。この順序は、クラスと呼ばれます。 自然順、およびクラスの{@codecompareTo}メソッドはそのと呼ばれます 自然比較法。
しかし、Student
の自然順序付けは何でしょうか?ファーストネーム?苗字?それらの組み合わせ?
数字についてはこの質問に答えるのは簡単ですが、Student
のようなクラスについては答えません。
ですから、Student
sはComparable
であるべきではないと思います。彼らは人間であり、日付や数字ではありません。そして、誰が大きいか、誰が等しいか、誰が小さいかを言うことはできません。
前に述べたが例として示されていない別のオプションは、グレードによる比較のために特別なComparator
を実装することです。
この例は、インターフェースStudent
を実装するクラスIStudent
、StudentGradeComparator
、およびサンプルデータを使用する小さなクラスMain
で構成されています。
詳細はコードコメントとして記載されていますので、お読みください
_/**
* A class that compares students by their grades.
*/
public class StudentGradeComparator implements Comparator<IStudent> {
@Override
public int compare(IStudent studentOne, IStudent studentTwo) {
int result;
int studentOneGrade = studentOne.getGrade();
int studentTwoGrade = studentTwo.getGrade();
/* The comparison just decides if studentOne will be placed
* in front of studentTwo in the sorted order or behind
* or if they have the same comparison value and are considered equal
*/
if (studentOneGrade > studentTwoGrade) {
/* larger grade is considered "worse",
* thus, the comparison puts studentOne behind studentTwo
*/
result = 1;
} else if (studentOneGrade < studentTwoGrade) {
/* smaller grade is considered "better"
* thus, the comparison puts studentOne in front of studentTwo
*/
result = -1;
} else {
/* the students have equal grades,
* thus, there will be no swap
*/
result = 0;
}
return result;
}
}
_
このクラスは、List
のsort(Comparator<? super IStudent> comparator)
メソッドで適用できます。
_/**
* The main class for trying out the sorting by Comparator
*/
public class Main {
public static void main(String[] args) {
// a test list for students
List<IStudent> students = new ArrayList<IStudent>();
// create some example students
IStudent beverly = new Student("Beverly", 3);
IStudent miles = new Student("Miles", 2);
IStudent william = new Student("William", 4);
IStudent deanna = new Student("Deanna", 1);
IStudent jeanLuc = new Student("Jean-Luc", 1);
IStudent geordi = new Student("Geordi", 5);
// add the example students to the list
students.add(beverly);
students.add(miles);
students.add(william);
students.add(deanna);
students.add(jeanLuc);
students.add(geordi);
// print the list in an unordered state first
System.out.println("———— BEFORE SORTING ————");
students.forEach((IStudent student) -> {
System.out.println(student.getName() + ": " + student.getGrade());
});
/*---------------------------------------*
* THIS IS HOW YOU APPLY THE COMPARATOR *
*---------------------------------------*/
students.sort(new StudentGradeComparator());
// print the list ordered by grade
System.out.println("———— AFTER SORTING ————");
students.forEach((IStudent student) -> {
System.out.println(student.getName() + ": " + student.getGrade());
});
}
}
_
完全を期すために、インターフェースIStudent
とその実装クラスStudent
を次に示します。
_public interface IStudent {
String getName();
int getGrade();
}
/**
* A class representing a student
*/
public class Student implements IStudent {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
_
グレードを非公開にしたい場合は、この方法で行うことができます。
students = students.stream().sorted((s1, s2) -> {
try {
Field f = s1.getClass().getDeclaredField("grade");
f.setAccessible(true);
int i = ((Integer)f.getInt(s1)).compareTo((Integer) f.get(s2));
f.setAccessible(false);
return i;
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
return 0;
}).collect(Collectors.toList());
ゲッターは悪い習慣ではありません、彼らはあなたの問題のために正確に作られています:それらを読むためにプライベートフィールドにアクセスする。
ゲッターを追加すると、次のことができます。
studentsList.stream().sorted((s1, s2) -> s1.getGrade()compareTo(s2.getGrade)).collect(Collectors.toList())
更新:本当に成績を非公開にしたい場合は、Comparable
を実装し、compare-methodをオーバーライドする必要があります。
ここでの最善のオプションは、並べ替えられたリストが必要な場所にComparator
を作成することです。これは、他の場所の他のフィールドで並べ替える必要があり、ドメインクラスが膨らむ可能性があるためです。
List<Student> sorted = list.stream()
.sorted(Comparator.comparingInt(o -> o.grade))
.collect(Collectors.toList());