英文:
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;
}
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
使用以下代码段替代原来的 List<Students> studentList = new ArrayList<Students>(pq);
将有助于确保将优先队列的确切顺序复制到列表中:
List<Students> studentList = new ArrayList<Students>();
while (!pq.isEmpty()) {
studentList.add(pq.poll());
}
英文:
So the question is as follows
A unique id is assigned to each student entering the queue. The queue serves the students based on the following criteria (priority criteria):
The student having the highest Cumulative Grade Point Average (CGPA) is served first.
Any students having the same CGPA will be served by name in ascending case-sensitive alphabetical order.
Any students having the same CGPA and name will be served in ascending order of the id
My code
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());
}
}
Sample input
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
Sample output
Dan
Ashley
Shafaet
Maria
I'm getting the following
Dan
Ashley
Maria
Shafaet
Edit: Using
List<Students> studentList = new ArrayList<Students>();
while(!pq.isEmpty())
{
studentList.add(pq.poll());
}
instead of List<Students> studentList = new ArrayList<Students>(pq); helps with copying the exact order of the PQ to the list.
答案1
得分: 2
比较器的一般结构应为:比较一个字段;如果字段值不同,返回;如果相同,继续下一个字段。
在这种情况下,可能会是这样的:
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;
(最后的 if
和 return
可以简化为 return cmp;
;但我认为如果按照上述方式进行,以后更容易扩展,因为您随后可以插入另一个 cmp/if
。)
英文:
The general structure of a comparator should be: compare a field; if the field values differ, return; if they are the same, proceed to the next field.
In this case, it could look something like:
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;
(The last if
and return
could be collapsed to just return cmp;
; but I think it's easier to extend later if you do it as above, because you can then just insert another cmp/if
.)
答案2
得分: 1
问题在于 PriorityQueue
的排序语义在其迭代器中不能得到保证(此外,注意 PriorityQueue
不是一个列表!)
从 Java 文档 中引用(重点是我的):
这个类及其迭代器实现了集合和迭代器接口的所有可选方法。在方法 iterator() 中提供的迭代器不能保证按任何特定顺序遍历优先级队列的元素。如果你需要有序遍历,请考虑使用 Arrays.sort(pq.toArray())。
这意味着除非你使用队列语义(如 add
、poll
、remove
、offer
等等),否则你将无法获得你的比较器的顺序。
从实现来看,调用 new ArrayList<Students>(pq);
(或者同样地,PriorityQueue#toString()
)是使用迭代器实现的,因此它不会遵守优先级。
这里的要点是:
- 仅在队列语义的情况下使用
PriorityQueue
是可以的,不能用于迭代/随机访问。 - 同时结合
List
和“实时”排序没有由标准 Java 类提供,至少我知道的没有(也可以使用比较器对列表进行排序,但没有每次添加元素时都会排序的列表)。但是,有这样的集合,使用Set
语义,参见TreeSet
,你可以在链接的答案中找到它们。需要注意的是,Set 语义是不同的(它们维护元素的唯一性,唯一性的含义有点复杂。例如,HashSet 的唯一性由 hashCode/equals 定义,但 TreeSet 的唯一性由比较器定义)。
话虽如此,Andy 的比较器实现要更可读(并且避免了重复)。
英文:
The issue is that PriorityQueue
's ordering semantic are not guaranteed in its iterator (on top of that, note that PriorityQueue is not a List !)
Quoting from the java doc (emphasis mine) :
> 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()).
Which means you will not get your comparator's ordering unless you use the queue semantics (e.g. add
, poll
, remove
, offer
... and the likes).
And looking at implementations, calling new ArrayList<Students>(pq);
(or PriorityQueue#toString()
for that matter) is implemented using an iterator, so it does not respect the priority.
The takeaway here :
- Using
PriorityQueue
is OK in the case of queue semantics only, not iteration / random access - Combining both
List
and "live" sorting is not provided by a standard Java class that I know of (see also). (You can sort a list using a comparator, but there is no list that is sorted each time you add an element). There are such collections, though, usingSet
semantics, seeTreeSet
, you'll find them on the linked answers. Be warned that Set semantics are different (they maintain unicity of their elements, the meaning of unicity being somewhat tricky to get. For example, HashSet unicity is defined by hashCode/equals, but TreeSet's is defined by a comparator's).
That being said, Andy's implementation of the comparator is by far more readable (and avoids repetitions).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论