Chapter 7

Chapter 7:
The Queue Abstract Data Type
Data Structures in Java: From Abstract Data Types
to the Java Collections Framework
by Simon Gray
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
Introduction
• Examples of Queue usage:
– Operating systems schedulers: processes can be placed in a ready queue
awaiting their turn to use the processor
– Print spoolers: print jobs on a first-come-first-served basis
– Simulations of any queue-like behavior (e.g., planes on a runway,
customers in a checkout line)
– Network packet buffering: packets arriving on the input port of a router
will be queued up on one of the router’s output port for the next hop
• Queue: a First-In-First-Out (FIFO) data structure – the first
item into the queue is the first out of it
– Attributes:
• front – the next element to remove from the queue
• rear – position where next element is to be inserted into the queue
– Operations: enqueue(element), dequeue(), peek(), isEmpty(), size()
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-2
Queue Properties and Attributes
Properties
1. Queues are FIFO data structures. All insertions are done at the rear and all
deletions at the front
2. The front always refers to the first element in the queue
3. The rear always refers to the position where the next element is to be inserted in
the queue
4. The queue has a fixed upper bound (capacity) on the number of elements it can
store
A Queue could be “boundless”. An upper bound is used to
illustrate the issues it raises, such as need for more attributes
Attributes
capacity : The maximum number of elements that can be in the queue at one time.
size : The number of elements in the queue: 0 ≤ size ≤ capacity at all times
front :
The first element of the queue or a null value not part of the queue
when the queue is empty
rear : The position in the queue where the next element is to be inserted or a
null value not part of the queue if the queue is full
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-3
Queue Operations
Note the change needed to
accommodate an upper bound
on the collection’s capacity
Queue()
pre-condition:
none
responsibilities: constructor - initialize the queue attributes
post-condition: size is set to 0
capacity is set to DEFAULT_CAPACITY
front refers to a null value (a special value not part of the queue)
rear refers the position where the first insertion is to be made
returns:
nothing
Queue( int maxElements )
pre-condition:
maxElements > 0
responsibilities: constructor — initialize the queue attributes
post-condition: size is set to 0
capacity is set to maxElements
front refers to a null value
rear refers to the position where the first insertion is to be made
returns:
nothing
throws:
invalid argument exception if pre-condition is not met
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-4
Queue Operations
Note the change needed to
accommodate an upper bound
on the collection’s capacity
enqueue( Type element )
pre-condition: isFull() is false
responsibilities: insert element at the rear of the queue
post-condition: element is placed at the rear of the queue
size is incremented by 1
rear is advanced to the position where the next element will be inserted or
some null value if the queue is full
returns:
nothing
throws:
full queue exception if pre-condition is not met
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-5
Queue Operations
dequeue()
pre-condition: isEmpty() is false
responsibilities: remove and return the element at front
post-condition: the front element is no longer in the queue
size is decremented by 1
front refers to the element that followed the previous front
element or null if the queue is empty
returns:
the element removed
throws:
empty queue exception if pre-condition is not met
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-6
Behavior of Queue’s operations
Method
Purpose
Returned
Value
New Object State
Queue<String> q =
new LinkedQueue<String>()
Create an empty Queue q
capacity = 5
size = 0
front = null rear = 1st queue slot
a LinkedQueue object for
Strings using the default
capacity
q.enqueue (“A”)
Add “A” to q; add an element to an empty
queue
size = 1
“A”
front rear = 2nd queue slot
q. enqueue (“B”)
Add “B” to q; add an element to a non-empty
queue
size = 2
“A” “B”
front = “A” rear = 3rd queue slot
q.isFull()
See if q is full
q. enqueue (“C”)
Add “C” to q
String str = q.peek()
Peek at the front element of q
str = q.dequeue()
Delete the front element from q
q.isEmpty()
See if q is empty
str = q.dequeue ()
Delete front element from q
size = 1
“C”
front = “C” rear = 2nd queue slot
“B”
str = q.dequeue ()
Delete front element from q
size = 0
front = null
“C”
false
size = 3
“A” “B” “C”
front = “A” rear = 4th queue slot
“A”
size = 2
“B” “C”
front = “B” rear = 3rd queue slot
“A”
false
rear = 1st queue slot
q.isEmpty()
See if q is empty
true
str = q.peek()
Peek at the front element of q
empty queue exception
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-7
Designing a Test Plan
The usual suspects:
– correct instantiation
– accessor methods return the correct values
– insertions really do insertions and in the right place
– deletions really do deletions and return the correct element
What does having an upper bound on size add?
– verify that there is no problem inserting and accessing
elements up to the queue’s capacity, paying special attention to
when the queue is at its capacity and when it is one element
short of capacity
– verify that an attempt to insert into a full queue generates the
expected error
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-8
Test Case 7.1: Enqueueing to capacity
Method
Purpose
Object State
Expected Result
Queue<String> q =
new LinkedQueue<String>(3)
Create an empty Queue with a
fixed capacity
capacity = 3
size = 0
front = null
rear = 1st queue slot
q.enqueue(“A”)
Enqueue into empty queue
size = 1
“A”
front = “A”
rear = 2nd queue slot
q.enqueue(“B”)
Enqueue into a nonempty queue
size = 2
“A” “B”
front = “A”
rear = 3rd queue slot
q.enqueue(“C”)
Enqueue into a nonempty queue
and reach the queue’s capacity
size = 3
“A” “B” “C”
front = “A”
rear = undefined
q.size()
Verify queue state
3
q.isEmpty()
Verify queue state
false
q.isFull()
Verify queue state
true
q.peek()
Verify queue state
“A”
q.enqueue(“oops”)
Verify test for pre-condition for
enqueue()
full queue exception
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
a LinkedQueue object for
Strings with a capacity of
3
7-9
ListQueue: Applying the Adapter Pattern
Tasks to complete to implement a Queue using a List
as the backing store:
1. Choose which implementation of List is most
appropriate for representing Queue
2. Decide which attributes from List will play the roles
of the Queue attributes
3. Select methods from List that will provide the
behavior needed for the Queue API
4. Deciding how to support Queue behavior or attributes
not supported by List
5. Define the exception classes FullQueueException
and EmptyQueueException
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-10
Choose the List Implementation to use
• What are the salient characteristics of a Queue?
– It is a linear structure that is accessed at both ends
• What are the salient characteristics of the List
implementations?
– ArrayList – accesses at index 0 are expensive while
accesses at length are cheap – O(1)
– LinkedList – accesses at either end are O(1) so long
as it is doubly-linked and we maintain a reference to
the head and tail of the list
LinkedList wins!
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-11
Map Queue Attributes to List Attributes
and/or Methods
Queue Attribute
List Attribute or Method
size
size()
front
head – position 0 of the list
rear
tail – position size() of the list
capacity
none
What are we going
to do about this?
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-12
Map Queue Methods to List Methods
Queue Operation
List Operation
void enqueue( element )
void add( list.size(), element )
E dequeue()
E remove( 0 )
E front()
E get( 0 )
int size()
int size()
boolean isEmpty()
boolean isEmpty()
void clear()
void clear()
boolean isFull()
none
capacity()
none
What are we going
to do about this?
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-13
Providing for Queue Characteristics Not
Supported by List Characteristics
• List has no support built in for handling an
upper bound on the number of elements that can
be in a collection
• The ListQueue implementation will have to
manage this itself
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-14
ListQueue: The UML Diagram
Compare this to the UML diagram
for the Adapter Design Pattern
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-15
What does the implementation look like?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package gray.adts.queue;
import java.util.LinkedList;
import java.util.List;
import java.lang.IllegalArgumentException;
/**
* Queue ADT implemented using the Adapter design pattern and
* java.util.LinkedList as the storage data structure. This
* version of Queue assumes an upper bound on the number of
* elements that can be stored in the queue.
*/
public class ListQueue<E> implements Queue<E> {
private List<E> queue;
private int capacity;
Let’s focus on aspects
relating to enforcing an
upper bound on size
/**
* Default constructor. Create an empty queue with the
* default capacity.
*/
public ListQueue() {
this( DEFAULT_CAPACITY );
}
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-16
What does the implementation look like?
67
68
69
70
71
72
73
74
75
76
Focus on enforcing an upper
bound on size
/**
* Add <tt>element</tt> to the end of the queue.
* @throws FullQueueException if the queue is full
*/
public void enqueue(E element) {
if ( this.isFull() ) {
throw new FullQueueException("The queue is full");
}
this.queue.add(this.queue.size(), element);
}
100 /**
101 * Determine if this queue has room for more elements.
102 * @return <tt>true</tt> if this queue has room for more elements
103 * (<tt>size() == capacity()</tt>); <tt>false</tt> otherwise.
104 */
105 public boolean isFull() {
106
return this.queue.size() == this.capacity;
107 }
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-17
Define Custom Exception Classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package gray.adts.queue;
Make it an unchecked
exception
/**
* Thrown when there is an attempt to access the front
* of an empty queue.
*/
public class EmptyQueueException extends RuntimeException {
public EmptyQueueException() {
super();
}
public EmptyQueueException( String errMsg ) {
super(“ “ + errMsg );
}
}
FullQueueException
would look very similar!
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-18
ArrayQueue: Using an Array underlying
data structure
A Queue is a linear structure that is accessed at both ends.
How do we map front and rear to the two ends of an array?
(a) Queue.front is always at 0 – shift elements left on dequeue().
(b) Queue.rear is always at 0 – shift elements right on enqueue().
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-19
ArrayQueue: Using an Array underlying
data structure
• Neither of these solutions is satisfactory as they
both involve moving data elements, which is
expensive
Idea: Instead of moving data elements to a fixed position
for front, let front advance through the array.
Hmmm….what do we do when front catches up with rear?
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-20
ArrayQueue: Using an Array underlying
data structure
Solution: Be more creative!
View the array as circular and
allow both front and rear to
advance through (around) the
array.
This will require no data
movement for enqueues or
dequeues!
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-21
An Array Implementation of Queue
Task List:
1. Map Queue ADT attributes to a circular array
implementation.
2. Implement the Queue ADT operations as
methods.
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-22
Queue attributes for array implementation
public class ArrayQueue<E> implements Queue<E> {
private E[] queue;
private int capacity;
private int size;
private int front;
private int rear;
This is the easy part.
Harder: to what should front and rear be initialized and
how should they be updated?
This is important because the implementation of a number
of operations will depend on the answer
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-23
Initializing Queue Attributes
• rear – where the next element will be inserted.
Initially the queue is empty, so start rear at 0
• front – trickier because the specification says it
has a “null” value when the queue is empty.
– Using -1would complicate the code
– We can rely on a test for empty to check for size == 0
– This frees us to initialize front to 0 and rely on other
safeguards to guarantee that front won’t be used until
it is meaningful to do so (e.g., the queue isn’t empty)
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-24
Updating Queue
It is really important to think
about these details. Many hours
Attributes
of debugging can be avoided!
front holds the index of the first element of the
queue
– doing a deletion means extracting the value at
position front in the array, then incrementing front
with wraparound when the end of the array is reached
rear holds the index in the array where the next
insertion will be made
– do the insertion, then increment rear (with the
wraparound), so that it is ready for the next enqueue()
operation
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-25
What does the implementation look like?
/**
* Insert element at the rear of the queue.
* @param element the element to be inserted
* post element is placed at the rear of the queue
* size is incremented by 1
* rear is position where the next element will be inserted
* @throws FullQueueException if isFull() is true
*/
public void enqueue( E element ) throws FullQueueException{
1. if the queue is full throw a FullQueueException
if ( this.isFull() )
throw new FullQueueException();
2. put element at the rear of the queue
this.queue[rear] = element;
pseudocode
3. advance rear with wraparound
this.rear = (this.rear + 1 ) % this.capacity;
4. increment size by 1
this.size++;
}
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-26
What does the implementation look like?
1 /**
2 * Empty the queue of elements. We own a method to remove
3 */
elements – use it!
4 public void clear() {
5
// empty the queue, making all the array cells null
6
while ( !this.isEmpty() )
7
this.dequeue();
8
// loop post-condition: this.size is 0
9
Remember, this is what we said
10
this.front = 0;
an empty queue looks like! All
11
this.rear = 0;
methods must be consistent with
12 }
one another!
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-27
Testing the Implementations
• Test cases were developed based on the API
• Additional test cases should be generated to
ensure that the wraparound feature works properly
– Insert elements up to capacity
– Remove some elements
– Insert additional elements forcing front and rear to
wraparound
– Check that these additional elements were inserted in
the correct spot and are available when they reach the
front
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-28
Implementation Evaluation
ListQueue
Operation
List
Operation
Cost
enqueue( element )
add( size(), element )
Ο(1)
enqueue( element )
Ο(1)
dequeue()
remove( 0 )
Ο(1)
dequeue()
Ο(1)
peek()
get( 0 )
Ο(1)
peek()
Ο(1)
clear()
clear()
Ο(1)
clear()
Ο(n)
The cost of ListQueue
operations
ArrayQueue
Operation
Cost
The cost of ArrayQueue
operations
Copyright © 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley
7-29