简体   繁体   中英

Scala priority queue not maintaining ordering

I want this to be in order based on price...

final case class Case(price: Int) {}

But it's actually a significantly bigger case class that I removed fields from. I want to sort it like this...

val queue = PriorityQueue.empty[Case](Ordering.by((_: Case).price).reverse)

^ Sorted by descending price.

Now I want that sorting to stay...

queue.enqueue(Case(price = 2))
println(queue.toString)

queue.enqueue(Case(price = 3))
println(queue.toString)

queue.enqueue(Case(price = 4))
println(queue.toString)

queue.enqueue(Case(price = 1))
println(queue.toString)

queue.enqueue(Case(price = 0))
println(queue.toString)

But my output isn't sorted on the fourth and fifth line...

PriorityQueue(Case(2))
PriorityQueue(Case(2), Case(3))
PriorityQueue(Case(2), Case(3), Case(4))
PriorityQueue(Case(1), Case(2), Case(4), Case(3))
PriorityQueue(Case(0), Case(1), Case(4), Case(3), Case(2))

Also, the foreach method isn't iterating in order...

queue.foreach{ q =>
  print(q + ", ")
}

Prints...

Case(0), Case(1), Case(4), Case(3), Case(2), 

How do I make my queue remain ordered on descending price?

Per the Scala Documentation , printing the queue will not show the priority:

Only the dequeue and dequeueAll methods will return methods in priority order (while removing elements from the heap). Standard collection methods including drop, iterator, and toString will remove or traverse the heap in whichever order seems most convenient.

Therefore, printing a PriorityQueue will not reveal the priority order of the elements, though the highest-priority element will be printed first. To print the elements in order, one must duplicate the PriorityQueue (by using clone, for instance) and then dequeue them

Thus if you want to see the elements in order you will need something like this:

scala> val queue = PriorityQueue.empty[Case](Ordering.by((_: Case).price).reverse)
queue: scala.collection.mutable.PriorityQueue[Case] = PriorityQueue()

scala> queue += Case(2) += Case(3) += Case(4) += Case(1) += Case(0)
res1: queue.type = PriorityQueue(Case(0), Case(1), Case(4), Case(3), Case(2))

scala> while (queue.size > 0) println(queue.dequeue)
Case(0)
Case(1)
Case(2)
Case(3)
Case(4)

Or you can get the ordered collection using dequeueAll :

scala> val queue = PriorityQueue.empty[Case](Ordering.by((_: Case).price).reverse)
queue: scala.collection.mutable.PriorityQueue[Case] = PriorityQueue()

scala> queue += Case(2) += Case(3) += Case(4) += Case(1) += Case(0)
res2: queue.type = PriorityQueue(Case(0), Case(1), Case(4), Case(3), Case(2))

scala> val ordered = queue.dequeueAll                                                                      
ordered: scala.collection.immutable.IndexedSeq[Case] = Vector(Case(0), Case(1), Case(2), Case(3), Case(4)) 

scala> ordered.foreach(println)                                                                            
Case(0)                                                                                                    
Case(1)                                                                                                    
Case(2)                                                                                                    
Case(3)                                                                                                    
Case(4)                     

Based on discussions here , there is no way to retrieve the elements in order without destroying the queue via dequeuing. This seems to be intrinsic to the implementation of the underlying data structure (a binary heap).

Printing the priority queue may not return the elements in the order.

What is guaranteed though is that head always returns the lowest (as per your ordering) element and repeated dequeue ing dequeues elements in the order specified by the Ordering .

When I do:

queue.dequeue()
queue.dequeue()
queue.dequeue()
queue.dequeue()
queue.dequeue()

I see:

res10: Case = Case(0)
res11: Case = Case(1)
res12: Case = Case(2)
res13: Case = Case(3)
res14: Case = Case(4)

Yes, the scala default implementation is not inplace dequeue while the priority queue elements are equal. You could use implement the inplace dequeue as blow:

class PriorityQueueTest{
 implicit val ord: Ordering[(Any,Int)] = Ordering.by(_._2)

 var queue = mutable.PriorityQueue[(Any,Int)]()

 def inplaceDeque(number:Int): Seq[(Any,Int)] ={
  var res: Seq[(Any,Int)] = Seq()
    res =queue.take(number).toSeq
    queue =queue.drop(number)
  res
 }
}

在此处输入图片说明

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