Как отсортировать список по приватному полю? - PullRequest
0 голосов
/ 03 сентября 2018

Мой класс сущности выглядит так:

public class Student {

   private int grade;

   // other fields and methods
 }

и я использую это так:

List<Student> students = ...;

Как мне отсортировать students по grade, учитывая, что это личное поле?

Ответы [ 10 ]

0 голосов
/ 11 сентября 2018

Вы можете сделать это так, если хотите сохранить приватность:

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());
0 голосов
/ 09 сентября 2018

Если вам действительно нужна сортировка по полю, к которому у вас нет доступа, вы можете использовать отражение :

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.

Также могут возникнуть проблемы, если вы запускаете этот код на modulepath для Java 9+ (вы можете получить InaccessibleObjectException брошенный).

О реализации Comparable

Из сопоставимых Документы :

Этот интерфейс накладывает полное упорядочение на объекты каждого класса, который его реализует. Этот порядок называется естественным порядком класса , а метод {@code compareTo} класса называется его естественным методом .

.

Но что может быть естественным порядком для Student? Имя? Фамилия? Их комбинация?

Легко ответить на этот вопрос для чисел, но не для таких классов, как Student.

Так что я не думаю, что Student должно быть Comparable, это люди, а не даты или числа. И вы не можете сказать, кто больше, кто равен, а кто меньше.

0 голосов
/ 10 сентября 2018

Полагаю, что лучший вариант здесь - создать Comparator там, где нужен отсортированный список, потому что вам может потребоваться сортировка по другим полям в других местах, и это приведет к увеличению класса вашего домена:

List<Student> sorted = list.stream()
    .sorted(Comparator.comparingInt(o -> o.grade))
    .collect(Collectors.toList());
0 голосов
/ 03 сентября 2018

Другой вариант, который был упомянут ранее, но не показан в качестве примера, - это реализация специального 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;
    }
}

Вы можете применить этот класс в 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 и класс его реализации 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;
    }

}
0 голосов
/ 08 сентября 2018

Опция, предоставляемая JDK 1.8, использует метод stream library sorted(), который не требует реализации интерфейса Comparable. Вам необходимо реализовать метод доступа (getter) для поля 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());

Второй компаратор вступает в игру, когда первый сравнивает два равных объекта.

0 голосов
/ 03 сентября 2018

Ваш класс может реализовать интерфейс 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);
  }

}
0 голосов
/ 03 сентября 2018

Реализация Сопоставимый интерфейс для класса Student и реализация метода int compareTo(T o). Таким образом, вы можете сохранить частную собственность класса.

0 голосов
/ 03 сентября 2018

У вас есть следующие варианты:

  1. сделать grade видимым
  2. определить метод получения для grade
  3. определить Comparator внутри Student
  4. Марка Student Реализация Comparable
  5. использовать отражение (по моему мнению, это не решение , это обходной путь / взломать )

Пример решения 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)
0 голосов
/ 03 сентября 2018

Геттеры не плохая практика, они созданы специально для вашей проблемы: доступ к закрытым полям для их чтения.
Добавить геттер, и вы можете сделать:

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

Обновление: если вы действительно хотите сохранить оценку в секрете, вам нужно реализовать Comparable и переопределить метод сравнения.

0 голосов
/ 03 сентября 2018

В общем, если вам нужно поведение, которое зависит от класса учащегося, эта информация должна быть доступна - добавьте метод или свойство, которое позволяет другому коду обращаться к нему.

Самое простое исправление будет таким:

public class Student implements IStudent {

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

    public int getGrade() {
        return grade;
    }
}

Возможно, вам следует расширить интерфейс IStudent:)

Однако, если вам это нужно только для сортировки, вы можете использовать идею, уже предложенную в других ответах: реализовать интерфейс Comparable. Таким образом, вы можете скрыть grade и использовать его внутри метода int compareTo.

...