私はPriorityQueueと自分のコンパレータを使用していますが、どういうわけか、最終結果が常に良いとは限りません。私はグレード平均、名前、id.noで並べ替える必要があります。最後に、順序付けされたキューに残っている名前を返します。残りの名前は問題ありませんが、順序は問題です。入力(名前、グレード平均、id.no):
add John 3,75 50
add Mark 3,8 24
add Shafaet 3,7 35
poll
poll
add Samiha 3,85 36
poll
add Ashley 3,9 42
add Maria 3,6 46
add Anik 3,95 49
add Dan 3,95 50
poll
予想される出力:
Dan
Ashley
Shafaet
Maria
私の結果:
Dan
Ashley
Maria
Shafaet
問題を見つけるのを手伝っていただけませんか?前もって感謝します!
class StComp implements Comparator<Students> {
@Override
public int compare(Students st1, Students st2) {
if (st1.getCgpa() == st2.getCgpa()) {
if (st1.getName().equals(st2.getName()))
return st1.getId() - st2.getId();
else
return st1.getName().compareTo(st2.getName());
}
else
return (st1.getCgpa() < st2.getCgpa()) ? 1 : -1;
}
}
StComp stComp = new StComp();
PriorityQueue<Students> pq = new PriorityQueue<Students>(2, stComp);
あなたのComparator
は正しいです。問題は、Iterator
を使用してリストを走査している可能性が高いことです。 PriorityQueue
documentation の状態:
メソッドiterator()で提供されるイテレーターは、特定の順序で優先度キューの要素をトラバースすることは保証されていません。
このようにPriorityQueue
を繰り返し処理すると、正しい結果が表示されます。
while (!pq.isEmpty())
System.out.println(pq.poll().getName());
}
この回答の最後に、完全に実証するための例を含めました。
PriorityQueue
を消去したくない場合にできることはいくつかあります。個人的には、PriorityQueue
の最初の選択が反復されることを意図していないため、ユースケースに対して正しくなかったため、どちらのアプローチもお勧めしません。
PriorityQueue
を配列にコピーし、Comparator
実装を使用してそれらをソートし、ソートされた配列を反復処理することができます。例:
Student[] students = pq.toArray(new Student[pq.size()]);
Arrays.sort(students, new StComp());
for (Student s : students) {
System.out.println(s.getName() + " " + s.getCgpa() + " " + s.getId());
}
または、ポーリング中にそれらをある種のCollection
に追加してから、それらをPriorityQueue
に戻します。例:
Collection<Student> temp = new LinkedList<>();
while (!pq.isEmpty()) {
Student s = pq.poll();
System.out.println(s.getName() + " " + s.getCgpa() + " " + s.getId());
temp.add(s);
}
pq.addAll(temp);
データを使用して示す例:
メイン
public class Main {
public static void main(String[] args) {
PriorityQueue<Student> pq = new PriorityQueue<>(new StComp());
pq.add(new Student("John", 75, 50)); // Student name, grade average, id
pq.add(new Student("Mark", 8, 24));
pq.add(new Student("Shafaet", 7, 35));
pq.poll();
pq.poll();
pq.add(new Student("Samiha", 85, 36));
pq.poll();
pq.add(new Student("Ashley", 9, 42));
pq.add(new Student("Maria", 6, 46));
pq.add(new Student("Anik", 95, 49));
pq.add(new Student("Dan", 95, 50));
pq.poll();
// Not guaranteed to be in priorty order
System.out.println("Using PriorityQueue's Iterator, may not be in the correct priority order.");
for (Student s : pq) {
System.out.println(s.getName() + " " + s.getCgpa() + " " + s.getId());
}
// Correct order, but removes from the Priority Queue
System.out.println("\nIterating until empty using PriorityQueue.poll(), will be in the correct order.");
while (!pq.isEmpty()) {
Student s = pq.poll();
System.out.println(s.getName() + " " + s.getCgpa() + " " + s.getId());
}
}
}
Student(名前を変更し、単数形にする必要があります)
public class Student {
private double cgpa;
private String name;
private int id;
public Student(String name, double cgpa, int id) {
this.name = name;
this.cgpa = cgpa;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public double getCgpa() {
return cgpa;
}
}
StComp(質問から変更されていないロジック)
public class StComp implements Comparator<Student> {
@Override
public int compare(Student st1, Student st2) {
if (st1.getCgpa() == st2.getCgpa()) {
if (st1.getName().equals(st2.getName())) {
return st1.getId() - st2.getId();
} else {
return st1.getName().compareTo(st2.getName());
}
} else {
return (st1.getCgpa() < st2.getCgpa()) ? 1 : -1;
}
}
}
出力(少なくとも私にとって、最初のIterator
バリアントでは結果が異なる場合があります)
Using PriorityQueue's Iterator, may not be in the correct priority order.
Dan 95.0 50
Ashley 9.0 42
Maria 6.0 46
Shafaet 7.0 35
Iterating until empty using PriorityQueue.poll(), will be in the correct order.
Dan 95.0 50
Ashley 9.0 42
Shafaet 7.0 35
Maria 6.0 46
Java 8以降、Comparatorクラス全体を次のように置き換えることができます。
_Comparator.comparingDouble(Students::getCgpa)
.thenComparing(Students::getName)
.thenComparingInt(Students::getId)
_
古いバージョンのJavaを使用している場合、または明示的なコンパレータを維持するように要求する場合は、等しい値に対してゼロを返す必要があります。また、 equals.と一致するようにコンパレータを記述する必要があります。/ documentation から:
一連の要素
c
にコンパレータS
が課す順序付けは、 equals と一貫していると言われ、c.compare(e1, e2)==0
は、S
のすべての_e1
_および_e2
_のe1.equals(e2)
と同じブール値を持っています。並べ替えられたセット(または並べ替えられたマップ)を並べ替えるために、equalsと矛盾する並べ替えを強制できるコンパレータを使用する場合は注意が必要です。明示的なコンパレータ
c
を使用してソートされたセット(またはソートされたマップ)が、セットS
から取り出された要素(またはキー)で使用されているとします。c
によってS
に課された順序が等号と一致しない場合、ソートされたセット(またはソートされたマップ)は「奇妙な」動作をします。特に、ソートされたセット(またはソートされたマップ)は、equals
で定義されているセット(またはマップ)の一般規約に違反します。
(要するに、2つのオブジェクトが等しい場合、Comparatorはそれらを比較するときにゼロを返す必要があります。)
_@Override
public int compare(Students st1, Students st2) {
int comparison = Double.compare(st1.getCgpa(), st2.getCgpa());
if (comparison == 0) {
comparison = st1.getName().compareTo(st2.getName());
}
if (comparison == 0) {
comparison = st1.getId() - st2.getId();
}
return comparison;
}
_
これは、Studentsクラスに対応するequals
メソッドがあることを前提としています。
_@Override
public boolean equals(Object obj) {
if (obj instanceof Students) {
Students other = (Students) obj;
return Double.compare(this.getCga(), other.getCga()) == 0
&& this.getName().equals(other.getName())
&& this.getId() == other.getId();
}
return false;
}
_