8.3 Array Based Queue

We can use simple array to store items as a queue.

  • enqueue items by assigning to the next available array index, back
  • dequeue them from the front index.

Leads to problems:

  • back may reach last array index,
  • when front reaches last array index, cannot enqueue anymore.

Solutions:

  • Shift values to front of array and update front and back.
  • When out of room, make new, bigger array and copy contents.

or

  • Think of array as circular.

Circular Array for Queue

What happens when front or back reach maxSize?

Indexing an element beyond the end of the array.

Instead, must do

front = (front + 1) % maxSize;
back = (back + 1) % maxSize;

But what if the array fills up?

Easy, make a new, bigger array.

But what if current queue spans across last and first array element?

  1. Make new, bigger array
  2. Copy items from old array starting at front and into new array starting at beginning of new array
    • Increment front in usual way (front+1) % maxQueueSize
    • Use counter to decide when to stop.
  3. Set front to 0
  4. Set back to size - 1
  5. set maxQueueSize to new array size

Implementation

Study the following code. First, the interface.

QueueInterface.java
public interface QueueInterface<T> {
 
   public boolean isEmpty();
   public void enqueue(T newItem);
   public T dequeue() throws QueueException;
   public T peek() throws QueueException;
 
}

Now here is an exception we can use.

QueueException.java
public class QueueException extends java.lang.RuntimeException {
 
   public QueueException(String s) {
      super(s);
   }
 
}

Now, the actual queue implementation. This java file includes a main function to test it.

Using a generic type for the elements of the array causes problems. We cannot do

  T [] items = new T[20];

Java compiler needs to know how much storage space to allocate. So, instead we can do

  T [] items = (T[]) new Object[20];

The java compiler will warn you about doing this, saying

Note: Queue.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

and if you compile with the recommended argument, you see

 warning: [unchecked] unchecked cast

at that line in the source code, among other things.

Queue.java
public class Queue<T> implements QueueInterface<T> {
 
   protected int maxQueueSize;
   protected T [] items;
   protected int front;
   protected int back;
   protected int size;
 
   public Queue(int maxSize) {
      maxQueueSize = maxSize;
      items = (T[]) new Object[maxQueueSize]; //because cannot do   new T[n]
      front = 0;
      back = maxQueueSize-1;
      size = 0;
   }
 
   public boolean isEmpty() {
      return size == 0;
   }
 
   public boolean isFull() {
      return size == maxQueueSize;
   }
 
   public void enqueue(T newItem) {
      if (isFull()) {
         // Resize the array
         int newSize = maxQueueSize * 2;
         T [] newItems = (T[]) new Object[newSize];
         // Copy items from old array at i to new array at k
         int i = front;
         for (int k = 0; k < size; k++) {
            newItems[k] = items[i];
            i = (i + 1) % maxQueueSize;
         }
         // Assign array variable, front and back indices, and maxQueueSize
         items = newItems;
         front = 0;
         back = size - 1;
         maxQueueSize = newSize;
      }
      // Assign newItem to element at back, after incrementing back
      back = (back + 1) % maxQueueSize;
      items[back] = newItem;
      ++size;
   }
 
   public T dequeue() throws QueueException {
      if (!isEmpty()) {
         T queueFront = items[front];
         front = (front + 1) % maxQueueSize;
         --size;
         return queueFront;
      } else {
         throw new QueueException("Queue: Attempt to dequeue an empty queue.");
      }
   }
 
   public T peek() throws QueueException {
      if (!isEmpty()) {
         return items[front];
      } else {
         throw new QueueException("Queue: Attempt to peek an empty queue.");
      }
   }
 
   public String toString() {
      String result = "Q: ";
      for (int i = front; i <= back; i = (i+1) % maxQueueSize) {
          result += items[i] + " ";
      }
      return result;
   }
 
   public String toStringDebug() {
      String result =    "    Q debug: ";
      String fbmarkers = "             ";
      for (int i = 0; i < maxQueueSize; i++) {
         T item = items[i];
         String itemString;
         if (item == null) {
            itemString = "_  ";
         } else {
            itemString = item.toString() + " ";
         }
         String fb = "";
         result += itemString;
         if (i == front) {
            int n = itemString.length() - 1;
            fb += "f";
         }
         if (i == back) {
            fb += "b";
         }
         int nBlanks = itemString.length() - fb.length();
         fb += (nBlanks <= 0)? "" : String.format("%" + nBlanks + "s", ""); // adding n blanks
         fbmarkers += fb;
      }
      return result + "\n" + fbmarkers;
   }
 
   public static void main(String [] args) {
 
      Queue<String> q = new Queue<String>(4);
 
      q.enqueue("a");
      System.out.println("\t\t\t\tenqueue(\"a\")");
      System.out.println(q.toStringDebug());
 
      q.enqueue("b");
      System.out.println("\t\t\t\tenqueue(\"b\")");
      System.out.println(q.toStringDebug());
 
      System.out.println("\n" + q + "\n");
 
      String removed = q.dequeue();
      System.out.println("\t\t\t\tdequeue() = \"" + removed + "\"");
      System.out.println(q.toStringDebug());
 
      System.out.println("\n" + q + "\n");
 
   }
 
}

Compile Queue.java and then run it. You should see

> java Queue
				enqueue("a")
    Q debug: a _  _  _  
             fb         
				enqueue("b")
    Q debug: a b _  _  
             f b       

Q: a b 
				dequeue() = "a"
    Q debug: a b _  _  
               fb      

Q: b 

The toStringDebug method shows the queue contents as the full array with f and b underneath marking the values of the indices front and back.

Study the enqueue method and make sure you understand how it works.

Further Testing and a Bug

Now add some code to main to further test this queue by adding more items and removing items. Test whether or not it correctly resizes the array.

Rather than doing a lot of copying and pasting, write a couple of simple methods to add an item, remove an item, and print the queue. With properly defined static methods, you should be able to replace the body of the main function with code like

      add(q,"a");
      remove(q);

that does the enqueue and dequeue and prints the information as done by the example code in main.

You should soon see a problem in the output. Figure out what the problem is and fix it.

Show the Instructor

When you have done the above steps, including fixing the bug, show the instructor that your code works. This is necessary to get your grade for this recitation.

Submit your Solution in RamCT

If you did not have time to show the instructor your working code, then you may submit it via RamCT, under “Recit Week 2”.

The bug is obvious if you do these operations:

      Queue<String> q = new Queue<String>(4);
      add(q,"a");
      add(q,"b");
      add(q,"c");
      add(q,"d");
      remove(q);
      add(q,"e");
      System.out.println("\n" + q + "\n");

You should see

				enqueue("a")
    Q debug: a _  _  _  
             fb         
				enqueue("b")
    Q debug: a b _  _  
             f b       
				enqueue("c")
    Q debug: a b c _  
             f   b    

Q: a b c 

				enqueue("d")
    Q debug: a b c d 
             f     b 
				dequeue() = "a"
    Q debug: a b c d 
               f   b 
				enqueue("e")
    Q debug: e b c d 
             b f     

Q: 

The last call to Queue.toString() didn't work! So, you must fix the implementation of Queue.toString(), so that this example works. Then, submit your Queue.java to the “Recit Week 2 Missed” assignment.

Recent changes RSS feed CC Attribution-Share Alike 3.0 Unported Driven by DokuWiki