A queue is a FIFO sequence. Addition takes place only at the tail, and removal takes place only at the head.

Operations


There's already a Queue interface in the Java Core API and a whole bunch of implementing subclasses, but we're going to write some queues from scratch because:
package edu.lmu.cs.rtoal.collections;
/**
* A small queue interface. You can query the size of the queue and
* ask whether it is empty, add and remove items, and peek at the front
* item.
*/
public interface Queue {
/**
* Adds the given item to the rear of the queue.
*/
void enqueue(Object item);
/**
* Removes the front item from the queue and returns it.
* @exception java.util.NoSuchElementException if the queue is empty.
*/
Object dequeue();
/**
* Returns the front item from the queue without popping it.
* @exception java.util.NoSuchElementException if the queue is empty.
*/
Object peek();
/**
* Returns the number of items currently in the queue.
*/
int size();
/**
* Returns whether the queue is empty or not.
*/
boolean isEmpty();
}
package edu.lmu.cs.rtoal.collections;
import java.util.LinkedList;
/**
* A trivial implementation of the simple queue interface, built as
* a wrapper around the LinkedList class from java.util.
*/
public class SimpleQueue implements Queue {
private LinkedList<Object> data = new LinkedList<Object>();
public void enqueue(Object item) {data.addLast(item);}
public Object dequeue() {return data.removeFirst();}
public Object peek() {return data.getFirst();}
public int size() {return data.size();}
public boolean isEmpty() {return data.isEmpty();}
}
This is a common design pattern and all developers are expected to know how to do this. Terminology associated with this technique:
Things to note here:
package edu.lmu.cs.rtoal.collections;
import java.util.NoSuchElementException;
/**
* An implementation of a queue using a fixed, non-expandable array whose
* capacity is set in its constructor.
*/
public class BoundedQueue implements Queue {
private Object[] array;
private int size = 0;
private int head = 0; // index of the current front item, if one exists
private int tail = 0; // index of next item to be added
public BoundedQueue(int capacity) {
array = new Object[capacity];
}
public void enqueue(Object item) {
if (size == array.length) {
throw new IllegalStateException("Cannot add to full queue");
}
array[tail] = item;
tail = (tail + 1) % array.length;
size++;
}
public Object dequeue() {
if (size == 0) {
throw new NoSuchElementException("Cannot remove from empty queue");
}
Object item = array[head];
array[head] = null;
head = (head + 1) % array.length;
size--;
return item;
}
public Object peek() {
if (size == 0) {
throw new NoSuchElementException("Cannot peek into empty queue");
}
return array[size - 1];
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
}
Queue q = new BoundedQueue(4);
q.enqueue("A");
q.enqueue("B");
q.enqueue("C");
q.dequeue();
q.enqueue("D");
q.dequeue();
q.enqueue("E");
q.enqueue("F");
This particular version uses dual head and tail pointers. Note here:
package edu.lmu.cs.rtoal.collections;
import java.util.NoSuchElementException;
/**
* An implementation of a queue using singly linked nodes. The
* queue itself maintains links to both the head and the tail
* node, so that both enqueuing and dequeueing are O(1).
*/
public class LinkedQueue implements Queue {
private class Node {
public Object data;
public Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
}
private Node head = null;
private Node tail = null;
public void enqueue(Object item) {
Node newNode = new Node(item, null);
if (isEmpty()) {head = newNode;} else {tail.next = newNode;}
tail = newNode;
}
public Object dequeue() {
if (isEmpty()) {
throw new NoSuchElementException();
}
Object item = head.data;
if (tail == head) {
tail = null;
}
head = head.next;
return item;
}
public Object peek() {
if (head == null) {
throw new NoSuchElementException();
}
return head.data;
}
public boolean isEmpty() {
return head == null;
}
public int size() {
int count = 0;
for (Node node = head; node != null; node = node.next) {
count++;
}
return count;
}
}
Here is what we need to test:
We really have three classes to test, but the test cases share a bunch of things in common; the common tests can go in a base class.
package edu.lmu.cs.rtoal.collections;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.NoSuchElementException;
import org.junit.Test;
/**
* Base test for any class that implements the Queue interface.
*/
public abstract class QueueTest {
/**
* The queue to use in all the tests: set this in subclasses.
*/
protected Queue q;
@Test
public void testNewQueueIsEmpty() {
assertTrue(q.isEmpty());
assertEquals(q.size(), 0);
}
@Test
public void testInsertsToEmptyQueue() {
int numberOfInserts = 6;
for (int i = 0; i < numberOfInserts; i++) {
q.enqueue("zzz");
}
assertTrue(!q.isEmpty());
assertEquals(q.size(), numberOfInserts);
}
@Test
public void testEnqueueThenDequeue() {
String message = "hello";
q.enqueue(message);
assertEquals(q.dequeue(), message);
}
@Test
public void testEnqueueThenPeek() {
String message = "hello";
q.enqueue(message);
int size = q.size();
assertEquals(q.peek(), message);
assertEquals(q.size(), size);
}
@Test
public void testFiftyInThenFiftyOut() {
for (int i = 0; i < 50; i++) {
q.enqueue(i);
}
for (int i = 0; i < 50; i++) {
assertEquals(((Integer)q.dequeue()).intValue(), i);
}
}
@Test
public void testRemovingDownToEmpty() {
int numberOfRemoves = (int)(Math.random() * 20 + 1);
for (int i = 0; i < numberOfRemoves; i++) {
q.enqueue("zzz");
}
for (int i = 0; i < numberOfRemoves; i++) {
q.dequeue();
}
assertTrue(q.isEmpty());
assertEquals(q.size(), 0);
}
@Test(expected=NoSuchElementException.class)
public void testRemoveOnEmptyQueue() {
assertTrue(q.isEmpty());
q.dequeue();
}
@Test(expected=NoSuchElementException.class)
public void testPeekIntoEmptyQueue() {
assertTrue(q.isEmpty());
q.dequeue();
}
}
package edu.lmu.cs.rtoal.collections;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for SimpleQueue.
*/
public class SimpleQueueTest extends QueueTest {
@Before
public void makeSimpleQueue() {
q = new SimpleQueue();
}
@Test public void stupidPieceOfCrapMethodForEclipse() {}
}
package edu.lmu.cs.rtoal.collections;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for LinkedQueue.
*/
public class LinkedQueueTest extends QueueTest {
@Before
public void makeLinkedQueue() {
q = new LinkedQueue();
}
@Test public void stupidPieceOfCrapMethodForEclipse() {}
}
package edu.lmu.cs.rtoal.collections;
import org.junit.Before;
import org.junit.Test;
/**
* Unit test for BoundedQueue.
*/
public class BoundedQueueTest extends QueueTest {
private static int CAPACITY = 60;
@Before
public void makeBoundedQueue() {
q = new BoundedQueue(CAPACITY);
}
@Test(expected=IllegalStateException.class)
public void testEnqueueToFullQueue() {
for (int i = 0; i < CAPACITY; i++) {
q.enqueue("abc");
}
q.enqueue("abc");
}
}