Java Collections.sort() 未按预期排序
Java Collections.sort() not sorting as expected
我正在尝试按特定属性对两个不同的对象 ArrayList 进行排序('Student' 个对象按 "program" 排序,'Professor' 个对象按 "faculty" 排序)。两个 classes 都扩展了我的摘要 'Person' class.
public abstract class Person implements Comparable<Person>{
private String name;
private String adress;
//getters, setters, etc., all works properly
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return name.compareTo(string);
}
}
然后,当我创建一个包含 1000000 个随机 'Person' 对象而不是学生或教授的数组时,我决定按他们的姓名字母顺序对其进行排序(这样工作正常)。
Person personByName[] = arrayPersonas.clone();
Arrays.sort(personByName);
然后,我将原来的 Person 数组分成两个 ArrayList,一个用于 Student 对象,另一个用于 Professor 对象:
ArrayList<Student> studentsByProgram = new ArrayList();
ArrayList<Professor> professorsByFaculty = new ArrayList();
for (int i = 0; i < 1000000; i++) {
if (arrayPersonas[i] instanceof Student) {
studentsByProgram.add((Student)arrayPersonas[i]);
} else {
professorsByFaculty.add((Professor)arrayPersonas[i]);
}
}
当我尝试按我想要的属性按字母顺序对每个 ArrayList 进行排序时,问题就来了,因为它一直按人名对它们进行排序:
Collections.sort(studentsByProgram);
Collections.sort(professorsByFaculty);
在这里我离开我的学生和教授 classes:
public class Student extends Person {
private String program;
private int year;
private double fee;
//constructor, setters, getters, toString, equals
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return program.compareTo(string);
}
@Override
public int compareTo(Person t) {
return super.compareTo(t.getName());
}
}
教授class:
public class Professor extends Person {
private String faculty;
private double salary;
//constructor, setters, getters, toString, equals
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return faculty.compareTo(string);
}
@Override
public int compareTo(Person t) {
return super.compareTo(t.getName());
}
}
我做错了什么?我想如果我在 Student 对象的 ArrayList 上调用 "Collections.sort()",它将使用我的 Student class 中的 "compareTo()" 方法,该方法使用 "program" 属性。我仍在学习使用这些方法,所以有些东西我没有得到。
问题
- 您没有定义应该如何比较
Person
个对象。
- 您错误地定义了
Student
和 Professor
实例的比较方式。
- 您编写的重载方法
compareTo(String)
具有误导性。
解决方案
正确定义Person#compareTo
,移除其compareTo(String)
:
public int compareTo(Person p) {
return getName().compareTo(p.getName());
}
正确定义 Student#compareTo
和 Professor#compareTo
,删除它们的 compareTo(String)
。下面是如何编写 Student#compareTo
的示例:
@Override
public int compareTo(Person t) {
final int personComparisonResult = super.compareTo(t);
if (personComparisonResult == 0) {
return program.compareTo(((Student) t).program);
}
return personComparisonResult;
}
它说 "compare them as Person
s first; if they are equal (here, have the same name), compare them as Student
s (here, by student's program)".
我会删除这些方法。对于不适合 class 域的简单代码行,不值得使用单独的方法。
如果您想使用与 类 "natural" 排序不同的顺序对对象进行排序,您应该使用 Arrays.sort(T[], Comparator<T>)
,以及实现特定的 Comparator
对象排序排序或排序。
Comparable
的 javadoc 解释了它应该实现的语义。 (仔细阅读!)
关于自然排序:
Person[]
的 "natural" 排序将由 compareTo(Person)
方法给出。
Student[]
(或ArrayList<Student>
)的"natural"排序将由compareTo(Student)
方法给出。
- 等等。
- 在 none 这些情况下,将使用您的
compareTo(String)
方法!
您有两个不同的 compareTo() 方法。 Collections.sort().
未调用您期望使用的那个
如果您想使用 Collections.sort() 对 Students 进行排序,那么您需要一个带有签名 compareTo(Student student);
的方法
此方法 "overlaps" 与 compareTo(Person person) 有两个问题:
在语义上,Person 级别的 compareTo() 方法建立了 语义 和您的 compareTo() 方法在学生水平偏离了那些语义,这绝不是一个好主意。
从技术上讲,您依赖与方法绑定相关的实现细节来使您的系统按预期运行。这充其量是狡猾的。
我会寻找一种使用显式用户提供的比较器的排序方法,而不是依赖于内部 compareTo() 的排序方法。
你在classPerson
中的compareTo(String)
方法没有多大意义,
因为它将 this
(Person
)与 String
进行比较。
特别是它 不 对由 class Person
.
实现的接口 Comparable<Person>
有贡献
您应该将 this
(一个 Person
)与另一个 Person
:
进行比较
@Override
public int compareTo(Person otherPerson) {
return name.compareTo(otherPerson.name);
}
然后,在你的 classes Professor
和 Student
你可以像这样使用上面的方法:
@Override
public int compareTo(Person otherPerson) {
return super.compareTo(otherPerson);
}
实际上,不再需要此方法,因为它的行为与Person
的compareTo(Person)
相同。
这个方法可以省略,效果一样。
现在是提醒自己 有效 Java 书第 40 条:始终如一地使用覆盖的好时机。
你的基础 class Person 没有在 compareTo 方法上使用 @Override 符号,所以你不会得到如果您实际上没有覆盖您认为的 compareTo 方法,则会出错。在这种情况下,参数类型是错误的。它应该是 Person,而不是 String。该方法未被调用,而是使用默认值。
-我
我相信当您想对 Class 对特定属性进行排序时,您需要使用比较器。
试试这样的东西:
static final Comparator<Student> compareProgram = new Comparator<Student>() {
public int compare(Student e1, Student e2) {
//condition ( you need to return the condition)
return e2.program().compareTo(e1.program());
}
};
// Employee database
static final Collection<Student> students = ... ;
public static void main(String[] args) {
List<Student> e = new ArrayList<Student>(students);
Collections.sort(e, compareProgram);
System.out.println(e);
}
比较器是一个存在于集合下的函数,因此您只需插入要查找的条件即可。
如果您无法实施,请告诉我。
我正在尝试按特定属性对两个不同的对象 ArrayList 进行排序('Student' 个对象按 "program" 排序,'Professor' 个对象按 "faculty" 排序)。两个 classes 都扩展了我的摘要 'Person' class.
public abstract class Person implements Comparable<Person>{
private String name;
private String adress;
//getters, setters, etc., all works properly
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return name.compareTo(string);
}
}
然后,当我创建一个包含 1000000 个随机 'Person' 对象而不是学生或教授的数组时,我决定按他们的姓名字母顺序对其进行排序(这样工作正常)。
Person personByName[] = arrayPersonas.clone();
Arrays.sort(personByName);
然后,我将原来的 Person 数组分成两个 ArrayList,一个用于 Student 对象,另一个用于 Professor 对象:
ArrayList<Student> studentsByProgram = new ArrayList();
ArrayList<Professor> professorsByFaculty = new ArrayList();
for (int i = 0; i < 1000000; i++) {
if (arrayPersonas[i] instanceof Student) {
studentsByProgram.add((Student)arrayPersonas[i]);
} else {
professorsByFaculty.add((Professor)arrayPersonas[i]);
}
}
当我尝试按我想要的属性按字母顺序对每个 ArrayList 进行排序时,问题就来了,因为它一直按人名对它们进行排序:
Collections.sort(studentsByProgram);
Collections.sort(professorsByFaculty);
在这里我离开我的学生和教授 classes:
public class Student extends Person {
private String program;
private int year;
private double fee;
//constructor, setters, getters, toString, equals
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return program.compareTo(string);
}
@Override
public int compareTo(Person t) {
return super.compareTo(t.getName());
}
}
教授class:
public class Professor extends Person {
private String faculty;
private double salary;
//constructor, setters, getters, toString, equals
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int compareTo(String string) {
return faculty.compareTo(string);
}
@Override
public int compareTo(Person t) {
return super.compareTo(t.getName());
}
}
我做错了什么?我想如果我在 Student 对象的 ArrayList 上调用 "Collections.sort()",它将使用我的 Student class 中的 "compareTo()" 方法,该方法使用 "program" 属性。我仍在学习使用这些方法,所以有些东西我没有得到。
问题
- 您没有定义应该如何比较
Person
个对象。 - 您错误地定义了
Student
和Professor
实例的比较方式。 - 您编写的重载方法
compareTo(String)
具有误导性。
解决方案
正确定义
Person#compareTo
,移除其compareTo(String)
:public int compareTo(Person p) { return getName().compareTo(p.getName()); }
正确定义
Student#compareTo
和Professor#compareTo
,删除它们的compareTo(String)
。下面是如何编写Student#compareTo
的示例:@Override public int compareTo(Person t) { final int personComparisonResult = super.compareTo(t); if (personComparisonResult == 0) { return program.compareTo(((Student) t).program); } return personComparisonResult; }
它说 "compare them as
Person
s first; if they are equal (here, have the same name), compare them asStudent
s (here, by student's program)".我会删除这些方法。对于不适合 class 域的简单代码行,不值得使用单独的方法。
如果您想使用与 类 "natural" 排序不同的顺序对对象进行排序,您应该使用 Arrays.sort(T[], Comparator<T>)
,以及实现特定的 Comparator
对象排序排序或排序。
Comparable
的 javadoc 解释了它应该实现的语义。 (仔细阅读!)
关于自然排序:
Person[]
的 "natural" 排序将由compareTo(Person)
方法给出。Student[]
(或ArrayList<Student>
)的"natural"排序将由compareTo(Student)
方法给出。- 等等。
- 在 none 这些情况下,将使用您的
compareTo(String)
方法!
您有两个不同的 compareTo() 方法。 Collections.sort().
未调用您期望使用的那个如果您想使用 Collections.sort() 对 Students 进行排序,那么您需要一个带有签名 compareTo(Student student);
的方法此方法 "overlaps" 与 compareTo(Person person) 有两个问题:
在语义上,Person 级别的 compareTo() 方法建立了 语义 和您的 compareTo() 方法在学生水平偏离了那些语义,这绝不是一个好主意。
从技术上讲,您依赖与方法绑定相关的实现细节来使您的系统按预期运行。这充其量是狡猾的。
我会寻找一种使用显式用户提供的比较器的排序方法,而不是依赖于内部 compareTo() 的排序方法。
你在classPerson
中的compareTo(String)
方法没有多大意义,
因为它将 this
(Person
)与 String
进行比较。
特别是它 不 对由 class Person
.
Comparable<Person>
有贡献
您应该将 this
(一个 Person
)与另一个 Person
:
@Override
public int compareTo(Person otherPerson) {
return name.compareTo(otherPerson.name);
}
然后,在你的 classes Professor
和 Student
你可以像这样使用上面的方法:
@Override
public int compareTo(Person otherPerson) {
return super.compareTo(otherPerson);
}
实际上,不再需要此方法,因为它的行为与Person
的compareTo(Person)
相同。
这个方法可以省略,效果一样。
现在是提醒自己 有效 Java 书第 40 条:始终如一地使用覆盖的好时机。
你的基础 class Person 没有在 compareTo 方法上使用 @Override 符号,所以你不会得到如果您实际上没有覆盖您认为的 compareTo 方法,则会出错。在这种情况下,参数类型是错误的。它应该是 Person,而不是 String。该方法未被调用,而是使用默认值。
-我
我相信当您想对 Class 对特定属性进行排序时,您需要使用比较器。
试试这样的东西:
static final Comparator<Student> compareProgram = new Comparator<Student>() {
public int compare(Student e1, Student e2) {
//condition ( you need to return the condition)
return e2.program().compareTo(e1.program());
}
};
// Employee database
static final Collection<Student> students = ... ;
public static void main(String[] args) {
List<Student> e = new ArrayList<Student>(students);
Collections.sort(e, compareProgram);
System.out.println(e);
}
比较器是一个存在于集合下的函数,因此您只需插入要查找的条件即可。
如果您无法实施,请告诉我。