1

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 studentList = new ArrayList(pq); helps with copying the exact order of the PQ to the list.

Fenzox
  • 334
  • 4
  • 20

2 Answers2

2

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

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • That is similar to what I did. I'm not sure which part of the logic went wrong for mine – Fenzox Sep 09 '20 at 10:07
  • @Fenzox Your code has `if ( this.getCgpa() == other.getCgpa()) return this.getName().compareTo(other.getName());` *before* you tell it to check if `this.getName().compareTo(other.getName()) == 0` – khelwood Sep 09 '20 at 10:11
  • @khelwood wouldn't the method stop checking after it has already returned? – Fenzox Sep 09 '20 at 10:14
  • @Fenzox Yes, it would have returned zero without getting to the bit comparing `getId()`. That's the problem. – khelwood Sep 09 '20 at 10:15
  • @khelwood I updated my code according to your suggestion but am still getting the same output – Fenzox Sep 09 '20 at 10:21
  • @Fenzox : this is precisely what Andy warned about. You're nesting if/else if/else without brackets, you're trying to shortcut cases and inevitably... you missed one branch (if `cgpa`s are equal compare `name`s... but what if names are also equal inside of this branch ?). The structure is here to help you, and future readers, understand precisely what is going on. Debugging Andy's implementation : <10 seconds. Debugging yours knowing there is a bug : 5 minutes, and we're not even sure yet. – GPI Sep 09 '20 at 10:21
  • @Fenzox I wasn't making a suggestion. I was answering your comment "I'm not sure which part of the logic went wrong for mine". My suggestion would be using the code from Andy's answer. – khelwood Sep 09 '20 at 10:24
  • @Fenzox the point about structuring it like this is that the comparisons of each of the fields are isolated, and not duplicated. With yours, things are all mixed up together, making it very hard to follow the logic. – Andy Turner Sep 09 '20 at 10:35
  • Yeah I understood your logic. I didn't know a better way to do it earlier. When I tried it with your logic the last 2 students are still showing up as maria and shafaet. Any idea? – Fenzox Sep 09 '20 at 10:37
1

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, using Set semantics, see TreeSet, 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).

GPI
  • 9,088
  • 2
  • 31
  • 38
  • Are you saying using ArrayList(pq) will not copy the order of values in pq exactly to the list? I used List studentList = new ArrayList(); while(!pq.isEmpty()) { studentList.add(pq.poll()); }. And that worked out for me – Fenzox Sep 09 '20 at 11:24
  • I am saying that new ArrayList(pq) uses an iterator on the queue, and that the queue's iterator does not reflect the comparator's ordering. This implies that the resulting ArrayList may or may not be ordered according to the comparator. When using queue semantics (poll is a method of queue) you are indeed getting elements in the comparator's order. – GPI Sep 09 '20 at 11:28