web-dev-qa-db-ja.com

プライベートフィールドでリストを並べ替える方法は?

私のエンティティクラスは次のようになります。

public class Student {

   private int grade;

   // other fields and methods
 }

そして私はそれをそのように使用します:

List<Student> students = ...;

プライベートフィールドであることを考慮して、studentsgradeで並べ替えるにはどうすればよいですか?

19
Fanta

次のオプションがあります。

  1. gradeを表示する
  2. gradeのゲッターメソッドを定義します
  3. ComparatorinsideStudentを定義します
  4. StudentComparableを実装させる
  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);

これは私のお気に入りのソリューションです。理由は次のとおりです。

  • 複数のComparatorsを簡単に定義できます
  • それは多くのコードではありません
  • あなたのフィールドはプライベートでカプセル化されたままです

解決策の例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 など)
42
slartidan

一般に、学生の成績に依存する動作が必要な場合は、この情報にアクセスできる必要があります。他のコードがアクセスできるようにするメソッドまたはプロパティを追加します。

したがって、最も簡単な修正は次のようになります。

public class Student implements IStudent {

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

    public int getGrade() {
        return grade;
    }
}

おそらくインターフェースIStudentも拡張する必要があります:)

ただし、これが並べ替えにのみ必要な場合は、他の回答ですでに提案されているアイデアを使用できます。実装 Comparable インターフェイス。このようにして、gradeを非表示にして、int compareToメソッド内で使用できます。

6
BartoszKP

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つの等しいオブジェクトを比較しているときに機能します。

6
akarimin

Studentクラスの Comparable interface を実装し、メソッドint compareTo(T o)を実装します。このようにして、グレードプロパティをプライベートに保つことができます。

4
Dhruvil Vaghela

クラスは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);
  }

}
4
Robert Kock

本当にアクセスできないフィールドで並べ替える必要がある場合は、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のようなクラスについては答えません。

ですから、StudentsはComparableであるべきではないと思います。彼らは人間であり、日付や数字ではありません。そして、誰が大きいか、誰が等しいか、誰が小さいかを言うことはできません。

4
caco3

前に述べたが例として示されていない別のオプションは、グレードによる比較のために特別なComparatorを実装することです。

この例は、インターフェースStudentを実装するクラスIStudentStudentGradeComparator、およびサンプルデータを使用する小さなクラス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;
    }
}
_

このクラスは、Listsort(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;
    }

}
_
3
deHaar

グレードを非公開にしたい場合は、この方法で行うことができます。

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

ゲッターは悪い習慣ではありません、彼らはあなたの問題のために正確に作られています:それらを読むためにプライベートフィールドにアクセスする。
ゲッターを追加すると、次のことができます。

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

更新:本当に成績を非公開にしたい場合は、Comparableを実装し、compare-methodをオーバーライドする必要があります。

2
ItFreak

ここでの最善のオプションは、並べ替えられたリストが必要な場所にComparatorを作成することです。これは、他の場所の他のフィールドで並べ替える必要があり、ドメインクラスが膨らむ可能性があるためです。

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