简体   繁体   中英

Why do I need Comparable for PriorityQueue?

I'm learning about Queue Collection and I don't understand why do I need to implement Comparable interface if I use PriorityQueue? I have used Comparable and Comparator interface in custom sorting for array/list. But why do I need to implement for a PriorityQueue when I don't want to sort it...

In the example below, I see that I can't add element in a PriorityQueue if I don't implement Comparable interface.

And the big missunderstanding for me, after I run this code, the list isn't sorted by id? I ask this question because I see that compareTo() method is overrided, and I use this method when I want to sort a list.

I remember that I have used exaclty this code when I've done a custom sorting for a list. And the list was sorted by id. Why the queue isn't sorted in this case?

Sorry for my english grammar. Any feedback will be apreciated!

import java.util.PriorityQueue;
import java.util.Queue;

    public class QueueExample {

        public static void main(String[] args) { 

            Queue<Book> queue = new PriorityQueue<>();

            //Creating Books  
            Book b1=new Book(121,"Let us C","Yashwant Kanetkar","BPB",8);  
            Book b2=new Book(233,"Operating System","Galvin","Wiley",6);  
            Book b3=new Book(101,"Data Communications & Networking","Forouzan","Mc Graw Hill",4); 
            Book b4=new Book(319,"Learn Java","Yanis Orhan","HEX",3);  
            Book b5=new Book(191,"Linux","Hadgy","Haman",7);  
            Book b6=new Book(287,"Python Programming","Tarzan","GEN",5); 

            //Adding Books to the queue  
            queue.add(b1);  
            queue.add(b2);  
            queue.add(b3);  
            queue.add(b4);  
            queue.add(b5);  
            queue.add(b6); 

            System.out.println("Traversing the queue elements:");  

            //Traversing queue elements  
            for(Book b : queue) {  

                System.out.println(b.id+" "+b.name+" "+b.author+" "+b.publisher+" "+b.quantity);  
            }  

            queue.remove();  
            System.out.println("After removing one book record:");  
            for(Book b : queue) { 

                System.out.println(b.id + " " + b.name + " " + b.author + " " + b.publisher + " " + b.quantity);  
            }  
        }  
    }

Book

public class Book implements Comparable<Book> {

    int id;  
    String name,author,publisher;  
    int quantity;  

    public Book(int id, String name, String author, String publisher, int quantity) {

        this.id = id;  
        this.name = name;  
        this.author = author;  
        this.publisher = publisher;  
        this.quantity = quantity;  
    }

    @Override
    public int compareTo(Book b) {

        if(id > b.id) {  

            return 1;  
        } else if (id < b.id) {  

            return -1;  
        } else {  

            return 0;  
        }
    }    
}

I see the head is the element with the lowest id, but after that there is no order. Why the queue isn't sorted by id?

Traversing the queue elements:
101 Data Communications & Networking Forouzan Mc Graw Hill 4
191 Linux Hadgy Haman 7
121 Let us C Yashwant Kanetkar BPB 8
319 Learn Java Yanis Orhan HEX 3
233 Operating System Galvin Wiley 6
287 Python Programming Tarzan GEN 5
After removing one book record:
121 Let us C Yashwant Kanetkar BPB 8
191 Linux Hadgy Haman 7
287 Python Programming Tarzan GEN 5
319 Learn Java Yanis Orhan HEX 3
233 Operating System Galvin Wiley 6

UPDATE

If I create an ArrayList and I sort it with the same compareTo() method and I print it with the same for-each loop, the list is sorted and it is printed in this order.

The code for an ArrayList:

import java.util.ArrayList;
import java.util.Collections;

public class BookExample {

    public static void main(String args[]) {

            ArrayList<Book> bookList = new ArrayList<>();

            Book b1=new Book(121,"Let us C","Yashwant Kanetkar","BPB",8);  
            Book b2=new Book(233,"Operating System","Galvin","Wiley",6);  
            Book b3=new Book(101,"Data Communications & Networking","Forouzan","Mc Graw Hill",4); 
            Book b4=new Book(319,"Learn Java","Yanis Orhan","HEX",3);  
            Book b5=new Book(191,"Linux","Hadgy","Haman",7);  
            Book b6=new Book(287,"Python Programming","Tarzan","GEN",5); 

            bookList.add(b1);  
            bookList.add(b2);  
            bookList.add(b3);  
            bookList.add(b4);  
            bookList.add(b5);  
            bookList.add(b6); 

            Collections.sort(bookList);

            for (Book b : bookList) {

              System.out.println(b.id + ", " + b.name + ", " + b.author + ", " 
              + b.publisher + ", " + b.quantity);
            }
          }
}

Book

public class Book implements Comparable<Book> {

    int id;  
    String name, author, publisher;  
    int quantity;  

      public Book(int id, String name, String author, String publisher, int quantity) {

        this.id = id;
        this.name = name;
        this.author = author;  
        this.publisher = publisher;  
        this.quantity = quantity;  
      }


    public int compareTo(Book b) {

            if(id > b.id) {  

                return 1;  
            } else if (id < b.id) {  

                return -1;  
            } else {  

                return 0;  
            }
      }
}

The result in the console:

101, Data Communications & Networking, Forouzan, Mc Graw Hill, 4
121, Let us C, Yashwant Kanetkar, BPB, 8
191, Linux, Hadgy, Haman, 7
233, Operating System, Galvin, Wiley, 6
287, Python Programming, Tarzan, GEN, 5
319, Learn Java, Yanis Orhan, HEX, 3

Why the for-each loop works very good when I print an ArrayList, and it doesn't work when I try to print a PriorityQueue?

Javadoc says :

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

As a result, you cannot just use a for(Book book: queue) loop.

You could copy into a temporary array as suggested in the Javadoc, or (if you don't mind destroying the queue in the process) loop over poll() .

I agree that this is very counter-intuitive. They should probably not implement Iterable at all...

A PriorityQueue is intended for a specific purpose: to allow you to take the highest 'priority' item from the queue irrespective of the order in which it was added. The comparison you supply is used only to determine the 'priority'. It's not an ordered collection in the sense of iterating through in an order.

You can test this by removing the items in turn and comparing them. You'll find that the order in which they are removed is defined by the comparison you defined.

PriorityQueue , as javadoc read

An unbounded priority queue based on a priority heap. The elements of the priority queue are ordered according to their natural ordering, or by a Comparator provided at queue construction time, depending on which constructor is used. A priority queue does not permit null elements. A priority queue relying on natural ordering also does not permit insertion of non-comparable objects (doing so may result in ClassCastException).

It needs a way to compare your element in order to correctly prioritize your elements when you add new element to the queue.

And to print the queue by order, you need to using following suggestion:

If you need ordered traversal, consider using Arrays.sort(pq.toArray()).

P/S: I think you just want an implementation of Queue interface in java, and suddenly jump to PriorityQueue .

In that case, ArrayDeque<> is best match for your need.

The output you're getting is correct, and let me explain to you why!!!

Few points about PriorityQueue:

  1. We can't create PriorityQueue of Objects that are non-comparable. This means that we can create PriorityQueue for Integers, String, or for other wrapper classes directly as they are comparable by default. However, if we want to create PriorityQueue for custom objects then we must have to implement a Comparable interface for that class to make custom objects comparable, as by default they are not comparable.
  2. PriorityQueue will not give us the sorted output. The purpose of PriorityQueue is only to give us either a top element or bottom element based on how we're giving comparing objects. While other elements of PriorityQueue will have random order.

I hope these 2 points answer your question.

The difference between a List and PriorityQueue is that whereas List keeps your element ordered by the insertion, PriorityQueue on other hand keeps your element ordered by the attribute you would like it to in your case (ID). If the elements that you are inserting cannot be compared with each other then a PriorityQueue behavior defaults to that of a list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM