如何按私有字段对列表排序?

时间:2018-09-03 12:21:55

标签: java list sorting oop encapsulation

我的实体类如下:

public class Student {

   private int grade;

   // other fields and methods
 }

我这样使用它:

List<Student> students = ...;

考虑到这是一个私有字段,如何将students排序为grade

10 个答案:

答案 0 :(得分:42)

您有以下选择:

  1. 使grade可见
  2. grade定义一个吸气方法
  3. 在内部定义Comparator Student
  4. 使Student实现Comparable
  5. 使用反射(我认为这不是解决方案,它是解决方法 / hack

解决方案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
  • 代码不多
  • 您的字段保持私密并封装

解决方案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

答案 1 :(得分:6)

通常,如果您需要的行为取决于学生的成绩,则此信息必须不可访问-添加允许其他代码访问它的方法或属性。

因此,最简单的解决方法是:

public class Student implements IStudent {

    ...
    private int grade;
    ...
    // other fields and methods

    public int getGrade() {
        return grade;
    }
}

您可能还应该扩展接口IStudent:)

但是,如果只需要排序,则可以采用其他答案中已经提出的想法:实现Comparable接口。这样,您可以将grade隐藏起来,并在int compareTo方法中使用它。

答案 2 :(得分:6)

JDK 1.8提供的一个选项正在使用streamsorted()方法,该方法不需要实现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类将是这样的:

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());

当第一个比较器比较两个相等的对象时,第二个比较器开始起作用。

答案 3 :(得分:4)

为Student类实施Comparable interface,并实现方法int compareTo(T o)。这样,您可以将成绩属性设置为私有。

答案 4 :(得分:4)

您的课程可以实现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);
  }

}

答案 5 :(得分:4)

如果您确实需要按字段排序,则无权访问,可以使用反射

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

  

此接口对实现该接口的每个类的对象强加了总体排序。此排序称为类的自然排序,而该类的{@code compareTo}方法被称为其自然比较方法

但是Student自然排序是什么? 名字?姓?他们的组合?

对于数字,很容易回答这个问题,但对于Student之类的类来说,不是很容易。

所以我不认为Student应该是Comparable,它们是人类,而不是日期或数字。而且你不能说谁更大,谁平等和谁更小。

答案 6 :(得分:3)

前面提到但未作为示例显示的另一个选项是实现特殊的' class ViewHolder { @BindView(R.id.title) TextView name; @BindView(R.id.job_title) TextView jobTitle; public ViewHolder(View view) { ButterKnife.bind(this, view); } @OnClick(R.id.submit) public void submit(View view) { // TODO submit data to server... } }' 以便按年级进行比较。

此示例包含一个实现接口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; } } 的{​​{1}}方法中应用此类:

sort(Comparator<? super IStudent> comparator)

仅出于完整性考虑,以下是接口List及其实现类/** * 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

答案 7 :(得分:2)

字母不是一个坏习惯,它们完全是针对您的问题而制作的:访问私有字段以读取它们。
添加吸气剂即可:

studentsList.stream().sorted((s1, s2) -> s1.getGrade()compareTo(s2.getGrade)).collect(Collectors.toList())  

更新:如果您确实想将成绩保密,则需要实现Comparable并覆盖比较方法。

答案 8 :(得分:2)

当您想保持成绩私密时,您可以采用这种方式:

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());

答案 9 :(得分:1)

我相信这里最好的选择是在需要排序列表的地方创建一个Comparator,因为您可能需要按其他位置的其他字段进行排序,这会使您的域类膨胀:

List<Student> sorted = list.stream()
    .sorted(Comparator.comparingInt(o -> o.grade))
    .collect(Collectors.toList());