简体   繁体   English

带有自定义Comparator的Java PriorityQueue

[英]Java PriorityQueue with custom Comparator

I am using a PriorityQueue and my own Comparator but somehow the end results are not always good. 我正在使用PriorityQueue和我自己的比较器,但不知何故最终结果并不总是好的。 I should sort by grade average, than name, than id.no. 我应按等级平均值而不是名字排序,而不是id.no. At the end it should return the names left in the queue ordered. 最后它应该返回队列中剩余的名称。 The remaining names are fine but their order is not. 其余的名字很好,但他们的顺序不是。 Input (name, grade avg, 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

Expected output: 预期产量:

Dan
Ashley
Shafaet
Maria

My result: 我的结果:

Dan
Ashley
Maria
Shafaet

Could you please help me to find the problem? 你能帮我找到问题吗? Thank you in advance! 先感谢您!

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

Your Comparator is correct. 你的Comparator是对的。 The issue is that you're most likely traversing the list using its Iterator . 问题是您最有可能使用其Iterator遍历列表。 The PriorityQueue documentation states: PriorityQueue文档说明:

The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. 方法iterator()中提供的迭代器不保证以任何特定顺序遍历优先级队列的元素。

If you were to iterate over your PriorityQueue like this, you should see the correct results: 如果你像这样迭代你的PriorityQueue ,你应该看到正确的结果:

while (!pq.isEmpty())
    System.out.println(pq.poll().getName());
}

I've included an example at the end of this answer to fully demonstrate. 我在这个答案的最后给出了一个例子来充分展示。


There are a couple of things you could do if you didn't want to clear your PriorityQueue . 如果您不想清除PriorityQueue可以执行以下几项操作。 Personally I wouldn't recommend either approach as the initial choice of a PriorityQueue was not correct for the use-case, as they are not intended to be iterated over. 我个人不推荐这两种方法,因为PriorityQueue的初始选择对于用例来说是不正确的,因为它们不打算迭代。

You could copy your PriorityQueue into an array, sort them using your Comparator implementation, iterate over the sorted array, eg: 您可以将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());
}

or add them to some sort of Collection whilst polling, then add them back to the PriorityQueue , eg: 或者在轮询时将它们添加到某种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);

The example using your data to demonstrate: 使用您的数据演示的示例:

Main 主要

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 (renamed, should be singular) 学生 (改名,应该是单数)

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 (logic unchanged from question) 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;
        }
    }
}

Output (for me at least, results may vary for the first Iterator variant) 输出 (至少对我来说,第一个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

As of Java 8, you can replace your entire Comparator class with this: 从Java 8开始,您可以用以下代码替换整个Comparator类:

Comparator.comparingDouble(Students::getCgpa)
    .thenComparing(Students::getName)
    .thenComparingInt(Students::getId)

If you are using an older version of Java, or insist on keeping the explicit Comparator, you must return zero for equal values. 如果您使用的是旧版本的Java,或者坚持保留显式比较器,则必须为相等的值返回零。 You also must write your Comparator so it is consistent with equals. 您还必须编写比较器以使其与equals一致。 From the documentation : 文档

The ordering imposed by a comparator c on a set of elements S is said to be consistent with equals if and only if c.compare(e1, e2)==0 has the same boolean value as e1.equals(e2) for every e1 and e2 in S . 当且仅当c.compare(e1, e2)==0具有与每个e1 e1.equals(e2)相同的布尔值时,比较器c对一组元素S施加的排序被称为与等于一致。 S e2e2

Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map). 当使用能够强加与equals不一致的排序的比较器来排序有序集(或有序映射)时,应该谨慎行事。 Suppose a sorted set (or sorted map) with an explicit comparator c is used with elements (or keys) drawn from a set S . 假设具有显式比较器c的有序集(或有序映射)与从集合S提取的元素(或键)一起使用。 If the ordering imposed by c on S is inconsistent with equals, the sorted set (or sorted map) will behave "strangely." 如果cS施加的排序与equals不一致,则排序集(或有序映射)将表现得“奇怪”。 In particular the sorted set (or sorted map) will violate the general contract for set (or map), which is defined in terms of equals . 特别是有序集(或有序映射)将违反集合(或映射)的一般契约,其以equals方式定义。

(In short, if two objects are equal, the Comparator must return zero when comparing them.) (简而言之,如果两个对象相等,比较器在比较它们时必须返回零。)

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

This assumes that your Students class has a matching equals method: 这假设您的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