簡體   English   中英

帶有自定義Comparator的Java PriorityQueue

[英]Java PriorityQueue with custom Comparator

我正在使用PriorityQueue和我自己的比較器,但不知何故最終結果並不總是好的。 我應按等級平均值而不是名字排序,而不是id.no. 最后它應該返回隊列中剩余的名稱。 其余的名字很好,但他們的順序不是。 輸入(名稱,等級avg,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文檔說明:

方法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());
        }
    }

}

學生 (改名,應該是單數)

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一致。 文檔

當且僅當c.compare(e1, e2)==0具有與每個e1 e1.equals(e2)相同的布爾值時,比較器c對一組元素S施加的排序被稱為與等於一致。 S e2e2

當使用能夠強加與equals不一致的排序的比較器來排序有序集(或有序映射)時,應該謹慎行事。 假設具有顯式比較器c的有序集(或有序映射)與從集合S提取的元素(或鍵)一起使用。 如果cS施加的排序與equals不一致,則排序集(或有序映射)將表現得“奇怪”。 特別是有序集(或有序映射)將違反集合(或映射)的一般契約,其以equals方式定義。

(簡而言之,如果兩個對象相等,比較器在比較它們時必須返回零。)

@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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM