我的可比接口逻辑有什么问题?

What is wrong with my comparable interface logic?

所以问题如下

每个进入队列的学生都会分配一个唯一的 ID。队列根据以下标准(优先级标准)为学生服务:

累积平均绩点 (CGPA) 最高的学生首先获得服务。

任何具有相同 CGPA 的学生将按姓名字母顺序升序服务。

任何具有相同 CGPA 和姓名的学生将按 ID 的升序排列

我的代码

class Priorities{
    public List<Students> getStudents(List<String> events) {
            PriorityQueue<Students> pq = new PriorityQueue<Students>();
            for ( String s : events) {
                if ( s.contains("ENTER")) {
                    String [] arr = s.split(" ");
                    int id = Integer.parseInt(arr[3]);
                    String name = arr[1];
                    Double cgpa = Double.parseDouble(arr[2]);       
                    pq.add(new Students(id, name, cgpa));
                }
                else
                    pq.poll();
            
            }
            List<Students> studentList = new ArrayList<Students>(pq);
    
            return studentList;
        }
        
        
        
    }
    
class Students implements Comparable<Students>{
    
    int id;
    String name;
    double cgpa;
    
    public Students(int id, String name, double cgpa) {
        this.id = id;
        this.name = name;
        this.cgpa = cgpa;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getCgpa() {
        return cgpa;
    }

    public void setCgpa(double cgpa) {
        this.cgpa = cgpa;
    }
        // -1 return left, 1 return right
    public int compareTo(Students other) {
    if ( this.equals(other))
        return 0;
    else if ( this.getCgpa() > other.getCgpa())
        return -1;
    else if ( this.getCgpa() < other.getCgpa())
        return 1;
    else if ( this.getCgpa() == other.getCgpa() && this.getName().compareTo(other.getName()) == 0)
        return Integer.compare(this.getId(), other.getId());
    else 
        return this.getName().compareTo(other.getName());
    
        
}
}

示例输入

12
ENTER John 3.75 50
ENTER Mark 3.8 24
ENTER Shafaet 3.7 35
SERVED
SERVED
ENTER Samiha 3.85 36
SERVED
ENTER Ashley 3.9 42
ENTER Maria 3.6 46
ENTER Anik 3.95 49
ENTER Dan 3.95 50
SERVED

示例输出

Dan
Ashley
Shafaet
Maria

我得到以下信息

Dan
Ashley
Maria
Shafaet

编辑:使用

List<Students> studentList = new ArrayList<Students>();
        while(!pq.isEmpty())
        {
            studentList.add(pq.poll());
        }

代替List studentList = new ArrayList(pq);帮助将 PQ 的确切顺序复制到列表中。

比较器的一般结构应该是:比较一个字段;如果字段值不同,return;如果它们相同,则继续下一个字段。

在这种情况下,它可能类似于:

int cmp;

cmp = Double.compare(other.getCgpa(), this.getCgpa());
if (cmp != 0) return cmp;

cmp = this.getName().compareTo(other.getName());
if (cmp != 0) return cmp;

cmp = Integer.compare(this.getId(), other.getId());
if (cmp != 0) return cmp;

return 0;

(最后的 ifreturn 可以折叠成 return cmp;; 但我认为如果你像上面那样做以后会更容易扩展,因为你可以插入另一个 cmp/if.)

问题是 PriorityQueue 的排序语义在其迭代器中得不到保证(最重要的是,请注意 PriorityQueue 不是列表!)

引自 java doc(强调我的):

This class and its iterator implement all of the optional methods of the Collection and Iterator interfaces. The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).

这意味着除非使用队列语义(例如 addpollremoveoffer... 以及喜欢)。

查看实现,调用 new ArrayList<Students>(pq);(或 PriorityQueue#toString())是使用迭代器实现的,因此它不考虑优先级。

这里的要点:

  • 仅在队列语义的情况下使用PriorityQueue是可以的,而不是迭代/随机访问
  • 结合 List 和“实时”排序 is not provided by a standard Java class that I know of (see also)。 (您可以使用比较器对列表进行排序,但是每次添加元素时都不会对列表进行排序)。但是,有这样的集合,使用 Set 语义,请参阅 TreeSet,您会在链接的答案中找到它们。请注意,Set 语义是不同的(它们保持其元素的唯一性,唯一性的含义有点难以理解。例如,HashSet 的唯一性由 hashCode/equals 定义,但 TreeSet 的由比较器定义)。

也就是说, 比较器的可读性要好得多(并且避免了重复)。