Login   /   Register

Implementing a Queue using a Dictionary

Rate this article
     3 votes, average: 3.67 out of 53 votes, average: 3.67 out of 53 votes, average: 3.67 out of 53 votes, average: 3.67 out of 53 votes, average: 3.67 out of 5
Loading ... Loading ...
March 29th, 2008 by Yaron Assa

A Queue is a particular type of collection containing entities that are kept in order. The principal (or only) operations on the collection are the addition of entities to the last position and removal of entities from the first position. This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that, whenever an element is added, all elements that were added before have to be removed before the new element can be invoked. A queue is an example of a linear data structure.<>

Queues provide services in computer science, transport and operations research where various entities such as data, messages, objects, persons, or events are stored and held to be processed later. In these contexts, the queue performs the function of a buffer.

Queues are common in computer programs, where they are implemented as data structures coupled with access routines, as an abstract data structure, or in object-oriented languages, as classes.

The queue implementation will create 2 entries in the associative Scripting.Dictionary to maintain pointers to the start and end of the queue [’curr’ and 'next]. 

The 'curr’ pointer points to the current element in the queue (the one that will be returned when the Pop function is called); while the 'next’ pointer points to the position where the next 'Push’ should put the item.

The only class member required for the task is the m_queue, which holds the Scripting.Dictionary object.

When the class is first instantiated, the Dictionary object is initialized with two entries "curr" and "next" equal to zero, meaning that the queue is empty.

Before the object is disposed of, the queue removes all items.


 

Class clsQueue

   Private m_queue ' Dictionary Private member

   ' ** Class Constructor

   Private Sub Class_Initialize  

      Set m_queue = CreateObject( "Scripting.Dictionary" )

      m_queue.Add "curr", 0

      m_queue.Add "next", 0

   End Sub

   ' ** Class Destructor

   Private Sub Class_Terminate  

      If m_queue.Count > 0 Then

         m_queue.RemoveAll

      End If

      Set m_queue = Nothing

   End Sub

   ' Add Properties Here

   ' Add Methods Here

End Class

The first public read-only property is Count. The Count property gets the number of elements contained in the Queue. The dictionary count always have more keys that the queue actual size, so a calculation is required.

Public Property Get Count()

   Count = m_queue( "next" ) - m_queue( "curr" )

End Property

Another useful property is IsEmpty. When looping the queue IsEmpty will return True, when Count = 0 and False otherwise.

Public Property Get IsEmpty()

   IsEmpty = CBool( Me.Count = 0 )

End Property

The main method of a Queue is the Push action. It actually Adds an object to the end of the Queue. When using the dictionary here’s the trick: the "next" entry is now logically points to the new entry. So that in

 fact the object is not added to the end of the Queue. The CLng function is to assure that an arithmetic calculation would take place.

Public Sub Push( ByVal data )

   m_queue.Item( m_queue.Item( "next" ) ) = data

   m_queue.Item( "next" ) = CLng( m_queue.Item( "next" ) ) + 1

End Sub

The second main method of a Queue is the Pop action. The Pop method removes and returns the object at the beginning of the Queue. When using the dictionary here’s the trick: the Dictionary returns the value pointed by "curr", and then removes the item from the Dictionary. But then "curr" points to the next item in the list.

Public Function Pop()

   If m_queue( "curr" ) >= m_queue( "next" ) Then

      Reporter.ReportEvent micFail, "Queue", "The queue is empty"

      Pop = Empty

   End If

   Pop = m_queue.Item( m_queue.Item( "curr" ) )

   m_queue.Remove m_queue( "curr" )

   m_queue.Item( "curr" ) = CLng( m_queue.Item( "curr" ) ) + 1

End Function

A utility method that I thought can be also useful in some cases is the Peek method. The Peek method returns the object at the beginning of the Queue without removing it. Very similar to Pop.

Public Function Peek()

   If m_queue( "curr" ) >= m_queue( "next" ) Then

      Reporter.ReportEvent micFail, "Queue", "The queue is empty"

      Peek = Empty

   End If

   Peek = m_queue( m_queue( "curr" ) )

End Function

This utility and the next one are inspirations received from the Dot.Net framework. I thought that if it’s good for Dot.Net, it must also be good for QTP.

To perform a cloning operation (that creates a shallow copy of the Queue.) you can’t just say Set m_clone = m_queue (inside the class) because after you set the class to Nothing, also m_clone will vanish. The idea of cloning is that the clone will live after the cloned (original) object "died". For this reason the creation of a new Dictionary is required, to which each item from the original Dictionary has to be copied.

Public Function Clone()

   Dim m_cloned, key

   Set m_cloned = CreateObject( "Scripting.Dictionary" )

   For Each key in m_queue.Keys

      m_cloned.Add key, m_queue( key )

   Next

   Set Clone = m_cloned

End Function

My last utility, again an inspiration from Dot.Net, is the ToArray method. This method takes an advantage of the Clone method we have seen before. Why do we need a clone object? The Items property of the Dictionary is an array which in this case contains also the "curr" and "next" items. But these are used solely for the purpose of handling the queue and, hence, they must be removed, but certainly not from the original object.


 

Public Function ToArray()

   Dim m_cloned

  

   Set m_cloned = Me.Clone()

   m_cloned.Remove( "curr" )

   m_cloned.Remove( "next" )

   ToArray = m_cloned.Items

End Function

Now, let’s review an example of how to implement the class.

Set queue = New clsQueue

Print "queue.Count : " & queue.Count     ' Prints zero

Print "queue.IsEmpty : " & queue.IsEmpty ' Prints True

' ** Adding a value

queue.Push "Message 1″

clip_image002

So, "curr" Points to "0″. The next item is pointed by "next".

After adding a second item :

clip_image004

"curr" still points to "0″, but "next" increased. And a last one:

clip_image006

clip_image008

Before "popping" all the items, let’s review some utilities:

Print "queue.Count : " & queue.Count       ' Prints 3

Print "queue.IsEmpty : " & queue.IsEmpty   ' Prints False

Print "queue.Peek : " & queue.Peek()       ' Prints "Message 1″

Print arr = queue.ToArray()

clip_image010

And lastly, somewhere in our code, we need to pop the items:

Do While queue.IsEmpty = False

   Print queue.Pop()

Loop

Set queue = Nothing

clip_image012

After the first Pop, "curr" is now pointing to "1″, entry "0″ was removed. "next" is still pointing to a future "3″ in case you push another item. After the second Pop:

clip_image014

And last "curr" is equal to "next" that’s why IsEmpty returns true, and Count returns 0.

clip_image016

 

Posted in Data Structures

Leave a Reply

You must be logged in to post a comment.

This article was viewed 787 times