如何按私有字段对列表进行排序?
How to sort a list by a private field?
我的实体 class 如下所示:
public class Student {
private int grade;
// other fields and methods
}
我是这样使用它的:
List<Student> students = ...;
如何根据 grade
对 students
进行排序,考虑到它是私有字段?
一般来说,如果您需要一种取决于学生成绩的行为,则必须可以访问此信息 - 添加允许其他代码访问它的方法或 属性。
最简单的修复方法是:
public class Student implements IStudent {
...
private int grade;
...
// other fields and methods
public int getGrade() {
return grade;
}
}
您或许还应该扩展接口 IStudent
:)
但是,如果您只需要它来进行排序,您可以采用其他答案中已经提出的想法:实现 Comparable
接口。这样你就可以隐藏 grade
,并在 int compareTo
方法中使用它。
Getters 不是一个坏习惯,它们正是为您的问题而设计的:访问私有字段以读取它们。
添加一个 getter 你可以这样做:
studentsList.stream().sorted((s1, s2) -> s1.getGrade()compareTo(s2.getGrade)).collect(Collectors.toList())
更新:如果你真的想将成绩保密,你需要实施Comparable
并覆盖比较方法。
您有以下选择:
- 使
grade
可见
- 为
grade
定义一个getter方法
- 定义一个
Comparator
里面Student
- 制作
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
)
为学生 class 实施 Comparable interface 并实施方法 int compareTo(T o)
。这样您就可以将成绩 属性 保密。
您的 class 可以实现 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);
}
}
之前提到但未作为示例显示的另一个选项是实施特殊 Comparator
以按等级进行比较。
这个例子包括一个 class Student
实现一个接口 IStudent
,一个 StudentGradeComparator
和一个使用 class 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)
方法中应用此 class
/**
* 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
及其实现 class 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;
}
}
JDK 1.8 提供的一个选项正在使用 stream
库 sorted()
方法,不需要实现 Comparable
接口。
您需要为字段 grade
实现访问器 (getter) 方法
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()
方法还允许您根据其他字段(如果有)对学生进行排序。例如,考虑一个用于学生的字段 name
,在这种情况下,您希望根据年级和姓名对 studentList 进行排序。所以 Student
class 会像这样:
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());
当第一个比较器比较两个相等的对象时,第二个比较器开始起作用。
如果您确实需要按您无权访问的字段排序,您可以使用 反射:
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:
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's {@code compareTo} method is referred to as its natural comparison method.
但是 Student
的 自然排序 可能是什么?
名?姓?他们的组合?
这个问题对于数字很容易回答,但对于 类 就不行了,比如 Student
。
所以我认为Student
不应该是Comparable
,他们是人而不是日期或数字。而且不能说谁大谁小谁平等
我认为这里最好的选择是在需要排序列表的地方创建一个 Comparator
,因为您可能需要在其他地方按其他字段排序,这会使您的域膨胀 class :
List<Student> sorted = list.stream()
.sorted(Comparator.comparingInt(o -> o.grade))
.collect(Collectors.toList());
如果你想将成绩保密,你可以这样做:
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());
我的实体 class 如下所示:
public class Student {
private int grade;
// other fields and methods
}
我是这样使用它的:
List<Student> students = ...;
如何根据 grade
对 students
进行排序,考虑到它是私有字段?
一般来说,如果您需要一种取决于学生成绩的行为,则必须可以访问此信息 - 添加允许其他代码访问它的方法或 属性。
最简单的修复方法是:
public class Student implements IStudent {
...
private int grade;
...
// other fields and methods
public int getGrade() {
return grade;
}
}
您或许还应该扩展接口 IStudent
:)
但是,如果您只需要它来进行排序,您可以采用其他答案中已经提出的想法:实现 Comparable
接口。这样你就可以隐藏 grade
,并在 int compareTo
方法中使用它。
Getters 不是一个坏习惯,它们正是为您的问题而设计的:访问私有字段以读取它们。
添加一个 getter 你可以这样做:
studentsList.stream().sorted((s1, s2) -> s1.getGrade()compareTo(s2.getGrade)).collect(Collectors.toList())
更新:如果你真的想将成绩保密,你需要实施Comparable
并覆盖比较方法。
您有以下选择:
- 使
grade
可见 - 为
grade
定义一个getter方法
- 定义一个
Comparator
里面Student
- 制作
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
)
为学生 class 实施 Comparable interface 并实施方法 int compareTo(T o)
。这样您就可以将成绩 属性 保密。
您的 class 可以实现 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);
}
}
之前提到但未作为示例显示的另一个选项是实施特殊 Comparator
以按等级进行比较。
这个例子包括一个 class Student
实现一个接口 IStudent
,一个 StudentGradeComparator
和一个使用 class 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)
方法中应用此 class
/**
* 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
及其实现 class 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;
}
}
JDK 1.8 提供的一个选项正在使用 stream
库 sorted()
方法,不需要实现 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()
方法还允许您根据其他字段(如果有)对学生进行排序。例如,考虑一个用于学生的字段 name
,在这种情况下,您希望根据年级和姓名对 studentList 进行排序。所以 Student
class 会像这样:
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());
当第一个比较器比较两个相等的对象时,第二个比较器开始起作用。
如果您确实需要按您无权访问的字段排序,您可以使用 反射:
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:
This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's {@code compareTo} method is referred to as its natural comparison method.
但是 Student
的 自然排序 可能是什么?
名?姓?他们的组合?
这个问题对于数字很容易回答,但对于 类 就不行了,比如 Student
。
所以我认为Student
不应该是Comparable
,他们是人而不是日期或数字。而且不能说谁大谁小谁平等
我认为这里最好的选择是在需要排序列表的地方创建一个 Comparator
,因为您可能需要在其他地方按其他字段排序,这会使您的域膨胀 class :
List<Student> sorted = list.stream()
.sorted(Comparator.comparingInt(o -> o.grade))
.collect(Collectors.toList());
如果你想将成绩保密,你可以这样做:
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());