24

I was trying to insert the integers in PriorityQueue, and I know that :

If no comparator is specified when a PriorityQueue is constructed, then the default comparator for the type of data stored in the queue is used. The default comparator will order the queue in ascending order

However, the output I am getting is not in sorted order. Output after running the below code is : [2, 4, 8, 6]

public static void main(String args[]) {
    PriorityQueue<Integer> q = new PriorityQueue<Integer>(10);
    q.offer(4);
    q.offer(2);
    q.offer(8);
    q.offer(6);

    System.out.print(q);
}

Can someone please explain why ?

stealthjong
  • 10,858
  • 13
  • 45
  • 84
Apprentice
  • 303
  • 1
  • 4
  • 10

5 Answers5

47

A PriorityQueue is what is called a binary heap. It is only ordered/sorted in the sense that the first element is the least. In other word, it only cares about what is in the front of the queue, the rest are "ordered" when needed.

The elements are only ordered as they are dequeued, i.e. removed from the queue using poll(). This is the reason why a PriorityQueue manages to have such good performance, as it is not doing any more sorting than it needs at any time.

If you want to know how heaps work in detail I recommend this MIT lecture on heaps.

Erik Vesteraas
  • 4,675
  • 2
  • 24
  • 37
  • "ordered as they are dequeued" this is what I was looking for but is apparently not correct: https://stackoverflow.com/questions/6952660/java-priority-queue-reordering-when-editing-elements "As you discovered, a priority queue does not resort all elements whenever an element is added or removed." – jMan Aug 30 '22 at 15:26
  • @jMan : It's is correct, but maybe not the most explicit: The elements are ordered as they are dequeued, but the only guarantee is that the dequeued element was the least when it was dequeued. Any remaining elements will indeed not be sorted by the action of dequeuing. – Erik Vesteraas Sep 26 '22 at 09:21
10

When you call System.out.print() on your PriorityQueue, it will not poll() your elements from it, but call toString(). PriorityQueue doesn't implement toString(), so it's the toString() from AbstractCollection which will be called:

public String toString() {
    Iterator<E> i = iterator();
if (! i.hasNext())
    return "[]";

StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
    E e = i.next();
    sb.append(e == this ? "(this Collection)" : e);
    if (! i.hasNext())
    return sb.append(']').toString();
    sb.append(", ");
}
}

As you can see, this method only iterate the PriorityQueue. As you can see in the PriorityQueue javadoc:

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

If you want to use the PriorityQueue as it is intented to be used, you have to poll() each value and print it:

while (!q.isEmpty()) {
    Integer i = q.poll();
    System.out.println(i);
}

Output:

2
4
6
8
Florent Bayle
  • 11,520
  • 4
  • 34
  • 47
3

That's because java.util.PriorityQueue implements a binary heap.

Unfortunately, there is no easy way to sort a PriorityQueue. If you poll() objects until the queue is empty, the elements will be in the comparator's order. That's why when I was faced with a similar problem I've implemented my own heap class that allows me to sort the elements after the fact; which I use for a top N list of a large number of elements.

(Since I made the class on the job, I don't have the right to post it here, but it's largely modeled after python's heap.py, so there's a good source of inspiration)

llogiq
  • 13,815
  • 8
  • 40
  • 72
1

An unbounded priority queue is based on a priority heap

Priority Queue.Offer() method uses siftUpComparable() internally to insert items when passed without comparator

siftUpComparable compares current element with all elements at Parent Positions(int i = paramInt - 1 >>> 1;) until the Heap condition is met


siftUpComparable Algorithm in Nutshell (if implemented through array Root=index 0):

1.Add the element to the bottom level of the heap.
2.Compare the added element with its parent; if they are in the correct order, stop.
3.If not, swap the element with its parent and return to the previous step.

Java Code

  private void siftUpComparable(int paramInt, E paramE)
  {
    Comparable localComparable = (Comparable)paramE;
    while (paramInt > 0)
    {
      int i = paramInt - 1 >>> 1;
      Object localObject = this.queue[i];
      if (localComparable.compareTo(localObject) >= 0) {
        break;
      }
      this.queue[paramInt] = localObject;
      paramInt = i;
    }
    this.queue[paramInt] = localComparable;
  }

In Your example:

q.offer(4); -> Inserts 4

Result: PQ[0]=4


q.offer(2); -> siftUpComparable compares 4 to 2 and swap positions (comparisons at Parent Locations)

Result: PQ[0]=2,PQ[1]=4


q.offer(8); -> siftUpComparable Compares 8 and 2 (since 2 is at Parent Location)

Result: PQ[2]=8


q.offer(6);: -> siftUp Compares 6 with 4 (Parent of location 3 is Location 1 according to paramInt - 1 >>> 1; logic )

Result: PQ[3]=6


Final PQ=[2, 4, 8, 6]

Ravi Yenugu
  • 3,895
  • 5
  • 40
  • 58
0

How java priority Queue is suppose to work?

Basically your print statement does not traverse the tree in order.

Community
  • 1
  • 1
coffeeaddict
  • 858
  • 5
  • 3
  • @Bergi Not exactly. `print()` traverses the array underlying the tree, in the array's order, not the tree's order. The tree is partially ordered. – user207421 Sep 02 '14 at 01:37