/* Copyright 1996-2008 Ariba, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. $Id: //ariba/platform/util/core/ariba/util/core/Deque.java#6 $ */ package ariba.util.core; import java.util.List; /** This class implements a double-ended queue (pronounced like "deck"). It has the behavior that each operation is in constant (amortized) time. It supports FIFO operation with the enqueue and dequeue methods, and LIFO with push and pop methods. Both access methods may be used on a single deque. This is a similar interface to the deque described in Intorduction to Algorithms by Cormen, Leiserson and Rivest: Eighth printing in chapter 11 (Elementary Data Structures) section 1 (Stacks and queues) @aribaapi private */ public class Deque implements DebugState { protected Object elementData[]; protected int headElement; protected int tailElement; protected int length; protected int count; /** Construct a deque with a default capacity. */ public Deque () { this(10); } /** Construct a deque with a given initial capacity. @param initialCapacity the minimum number of elements that the deque is required to hold before having to grow the internal storage. */ public Deque (int initialCapacity) { Assert.that(initialCapacity > 0, "Deque must be able to hold a positive number of elements"); length = initialCapacity; headElement = 0; tailElement = 0; elementData = new Object[length]; } /** Enqueues an object into the deque. @param o the object to enqueue */ public void enqueue (Object o) { if (count == length) { setSize(length * 2); } elementData[tailElement] = o; tailElement++; if (tailElement >= length) { tailElement = 0; } count++; } /** Dequeues from the deque. @return the oldest element in the deque. */ public Object dequeue () { Assert.that(count > 0, "Queue underfow"); Object o = elementData[headElement]; // don't leak memory elementData[headElement] = null; headElement++; if (headElement >= length) { headElement = 0; } count--; return o; } /** Peek in the deque without removing it. @return the oldest element in the deque. */ public Object peekDequeue () { Assert.that(count > 0, "Queue underfow"); return elementData[headElement]; } /** Pushes an object onto the deque in the manner of a stack. This method is a synonym for enqueue. @param o the object to enqueue @see #enqueue(Object) */ public void push (Object o) { enqueue(o); } /** Peek into the deque without removing any elements. @return the youngest element in the deque */ public Object peekPop () { Assert.that(count > 0, "Queue underfow"); int index = tailElement - 1; if (index < 0) { index = length-1; } Object o = elementData[index]; return o; } /** Pops an element from the deque. @return the youngest element in the deque */ public Object pop () { Assert.that(count > 0, "Queue underfow"); tailElement--; if (tailElement < 0) { tailElement = length-1; } count--; Object o = elementData[tailElement]; // don't leak memory elementData[tailElement] = null; return o; } /** Set the size for the internal storage. This may be larger or smaller than the current size. @param size the new size for the internal storage. This must be greater than or equal to the count and may not be zero. */ public void setSize (int size) { Assert.that(size >= count, "May not shrink a queue to be smaller than the " + "count of elements."); Assert.that(size > 0, "size must be greater than zero"); Object newElementData[] = new Object[size]; // calculate the number of items to the right of the head // to copy. int tailCount=Math.min(length-headElement, count); System.arraycopy(elementData, headElement, newElementData, 0, tailCount); // copy all the items remaining if they weren't all copied // already if (tailCount < count) { System.arraycopy(elementData, 0, newElementData, tailCount, tailElement); } headElement = 0; tailElement = count; if (tailElement == size) { tailElement = 0; } elementData = newElementData; length = size; } /** Check the number of elements in the deque. @return the number of elements in the deque. */ public int count () { return size(); } /** Check the number of elements in the deque. @return the number of elements in the deque. */ public int size () { return count; } /** Check if the deque is empty. @return <code>true</code> if and only if the deque is empty. */ public boolean isEmpty () { return count == 0; } /** Iterates through the deque from head to tail and calls toString on each node */ public Object debugState () { List v = ListUtil.list(count); int i = 0; int pos = headElement; while (i < count) { v.add(elementData[pos]); i++; if (pos++ >= length) { pos = 0; } } return v; } public String toString () { return debugState().toString(); } }