Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Circular Queue extends AbstractList

/*  *  Licensed to the Apache Software Foundation (ASF) under one  *  or more contributor license agreements.  See the NOTICE file  *  distributed with this work for additional information  *  regarding copyright ownership.  The ASF licenses this file  *  to you 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.   *    */ import java.io.Serializable; import java.util.AbstractList; import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; /**  * A unbounded circular queue based on array.  *   * @author The Apache MINA Project (dev@mina.apache.org)  * @version $Rev: 762170 $, $Date: 2009-04-06 00:01:18 +0200 (Mon, 06 Apr 2009) $  */ public class CircularQueue<E> extends AbstractList<E> implements List<E>, Queue<E>, Serializable {     /** The serialVersionUID : mandatory for serializable classes */     private static final long serialVersionUID = 3993421269224511264L;     /** Minimal size fo the underlying attay */     private static final int DEFAULT_CAPACITY = 4;     /** The initial capacity of the list */     private final int initialCapacity;          // XXX: This volatile keyword here is a workaround for SUN Java Compiler bug,     //      which produces buggy byte code.  I don't event know why adding a volatile     //      fixes the problem.  Eclipse Java Compiler seems to produce correct byte code.     private volatile Object[] items;     private int mask;     private int first = 0;     private int last = 0;     private boolean full;     private int shrinkThreshold;     /**      * Construct a new, empty queue.      */     public CircularQueue() {         this(DEFAULT_CAPACITY);     }          public CircularQueue(int initialCapacity) {         int actualCapacity = normalizeCapacity(initialCapacity);         items = new Object[actualCapacity];         mask = actualCapacity - 1;         this.initialCapacity = actualCapacity;         this.shrinkThreshold = 0;     }     /**      * The capacity must be a power of 2.      */     private static int normalizeCapacity(int initialCapacity) {         int actualCapacity = 1;                  while (actualCapacity < initialCapacity) {             actualCapacity <<= 1;             if (actualCapacity < 0) {                 actualCapacity = 1 << 30;                 break;             }         }         return actualCapacity;     }     /**      * Returns the capacity of this queue.      */     public int capacity() {         return items.length;     }     @Override     public void clear() {         if (!isEmpty()) {             Arrays.fill(items, null);             first = 0;             last = 0;             full = false;             shrinkIfNeeded();         }     }     @SuppressWarnings("unchecked")     public E poll() {         if (isEmpty()) {             return null;         }         Object ret = items[first];         items[first] = null;         decreaseSize();                  if (first == last) {             first = last = 0;         }         shrinkIfNeeded();         return (E) ret;     }     public boolean offer(E item) {         if (item == null) {             throw new NullPointerException("item");         }                  expandIfNeeded();         items[last] = item;         increaseSize();         return true;     }     @SuppressWarnings("unchecked")     public E peek() {         if (isEmpty()) {             return null;         }         return (E) items[first];     }     @SuppressWarnings("unchecked")     @Override     public E get(int idx) {         checkIndex(idx);         return (E) items[getRealIndex(idx)];     }     @Override     public boolean isEmpty() {         return (first == last) && !full;     }     @Override     public int size() {         if (full) {             return capacity();         }                  if (last >= first) {             return last - first;         } else {             return last - first + capacity();         }     }          @Override     public String toString() {         return "first=" + first + ", last=" + last + ", size=" + size()                 + ", mask = " + mask;     }     private void checkIndex(int idx) {         if (idx < 0 || idx >= size()) {             throw new IndexOutOfBoundsException(String.valueOf(idx));         }     }     private int getRealIndex(int idx) {         return (first + idx) & mask;     }     private void increaseSize() {         last = (last + 1) & mask;         full = first == last;     }     private void decreaseSize() {         first = (first + 1) & mask;         full = false;     }     private void expandIfNeeded() {         if (full) {             // expand queue             final int oldLen = items.length;             final int newLen = oldLen << 1;             Object[] tmp = new Object[newLen];                  if (first < last) {                 System.arraycopy(items, first, tmp, 0, last - first);             } else {                 System.arraycopy(items, first, tmp, 0, oldLen - first);                 System.arraycopy(items, 0, tmp, oldLen - first, last);             }                  first = 0;             last = oldLen;             items = tmp;             mask = tmp.length - 1;             if (newLen >>> 3 > initialCapacity) {                 shrinkThreshold = newLen >>> 3;             }         }     }          private void shrinkIfNeeded() {         int size = size();         if (size <= shrinkThreshold) {             // shrink queue             final int oldLen = items.length;             int newLen = normalizeCapacity(size);             if (size == newLen) {                 newLen <<= 1;             }                          if (newLen >= oldLen) {                 return;             }                          if (newLen < initialCapacity) {                 if (oldLen == initialCapacity) {                     return;                 } else {                     newLen = initialCapacity;                 }             }                          Object[] tmp = new Object[newLen];                  // Copy only when there's something to copy.             if (size > 0) {                 if (first < last) {                     System.arraycopy(items, first, tmp, 0, last - first);                 } else {                     System.arraycopy(items, first, tmp, 0, oldLen - first);                     System.arraycopy(items, 0, tmp, oldLen - first, last);                 }             }                  first = 0;             last = size;             items = tmp;             mask = tmp.length - 1;             shrinkThreshold = 0;         }     }     @Override     public boolean add(E o) {         return offer(o);     }     @SuppressWarnings("unchecked")     @Override     public E set(int idx, E o) {         checkIndex(idx);         int realIdx = getRealIndex(idx);         Object old = items[realIdx];         items[realIdx] = o;         return (E) old;     }     @Override     public void add(int idx, E o) {         if (idx == size()) {             offer(o);             return;         }         checkIndex(idx);         expandIfNeeded();         int realIdx = getRealIndex(idx);         // Make a room for a new element.         if (first < last) {             System                     .arraycopy(items, realIdx, items, realIdx + 1, last                             - realIdx);         } else {             if (realIdx >= first) {                 System.arraycopy(items, 0, items, 1, last);                 items[0] = items[items.length - 1];                 System.arraycopy(items, realIdx, items, realIdx + 1,                         items.length - realIdx - 1);             } else {                 System.arraycopy(items, realIdx, items, realIdx + 1, last                         - realIdx);             }         }         items[realIdx] = o;         increaseSize();     }     @SuppressWarnings("unchecked")     @Override     public E remove(int idx) {         if (idx == 0) {             return poll();         }         checkIndex(idx);         int realIdx = getRealIndex(idx);         Object removed = items[realIdx];         // Remove a room for the removed element.         if (first < last) {             System.arraycopy(items, first, items, first + 1, realIdx - first);         } else {             if (realIdx >= first) {                 System.arraycopy(items, first, items, first + 1, realIdx                         - first);             } else {                 System.arraycopy(items, 0, items, 1, realIdx);                 items[0] = items[items.length - 1];                 System.arraycopy(items, first, items, first + 1, items.length                         - first - 1);             }         }         items[first] = null;         decreaseSize();         shrinkIfNeeded();         return (E) removed;     }     public E remove() {         if (isEmpty()) {             throw new NoSuchElementException();         }         return poll();     }     public E element() {         if (isEmpty()) {             throw new NoSuchElementException();         }         return peek();     } } /////////////////////////////////////////////// /*  *  Licensed to the Apache Software Foundation (ASF) under one  *  or more contributor license agreements.  See the NOTICE file  *  distributed with this work for additional information  *  regarding copyright ownership.  The ASF licenses this file  *  to you 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.   *    */ package org.apache.mina.util; import junit.framework.TestCase; import java.util.Iterator; /**  * Tests {@link org.apache.mina.util.CircularQueue}  *   * @author The Apache MINA Project (dev@mina.apache.org)  * @version $Rev: 755170 $, $Date: 2009-03-17 10:46:58 +0100 (Tue, 17 Mar 2009) $  */ public class CircularQueueTest extends TestCase {     private volatile int pushCount;     private volatile int popCount;     public void setUp() {         pushCount = 0;         popCount = 0;     }     public void testRotation() {         CircularQueue<Integer> q = new CircularQueue<Integer>(); // DEFAULT_CAPACITY = 4         testRotation0(q);     }     public void testExpandingRotation() {         CircularQueue<Integer> q = new CircularQueue<Integer>(); // DEFAULT_CAPACITY = 4         for (int i = 0; i < 10; i++) {             testRotation0(q);             // make expansion happen             int oldCapacity = q.capacity();             for (int j = q.capacity(); j >= 0; j--) {                 q.offer(new Integer(++pushCount));             }             assertTrue(q.capacity() > oldCapacity);             testRotation0(q);         }     }     private void testRotation0(CircularQueue<Integer> q) {         for (int i = 0; i < q.capacity() * 7 / 4; i++) {             q.offer(new Integer(++pushCount));             assertEquals(++popCount, q.poll().intValue());         }     }     public void testRandomAddOnQueue() {         CircularQueue<Integer> q = new CircularQueue<Integer>();         // Create a queue with 5 elements and capacity 8;         for (int i = 0; i < 5; i++) {             q.offer(new Integer(i));         }         q.add(0, new Integer(100));         q.add(3, new Integer(200));         q.add(7, new Integer(300));         Iterator<Integer> i = q.iterator();         assertEquals(8, q.size());         assertEquals(new Integer(100), i.next());         assertEquals(new Integer(0), i.next());         assertEquals(new Integer(1), i.next());         assertEquals(new Integer(200), i.next());         assertEquals(new Integer(2), i.next());         assertEquals(new Integer(3), i.next());         assertEquals(new Integer(4), i.next());         assertEquals(new Integer(300), i.next());         try {             i.next();             fail();         } catch (Exception e) {             // an exception signifies a successfull test case             assertTrue(true);                     }     }     public void testRandomAddOnRotatedQueue() {         CircularQueue<Integer> q = getRotatedQueue();         q.add(0, new Integer(100)); // addFirst         q.add(2, new Integer(200));         q.add(4, new Integer(300));         q.add(10, new Integer(400));         q.add(12, new Integer(500)); // addLast         Iterator<Integer> i = q.iterator();         assertEquals(13, q.size());         assertEquals(new Integer(100), i.next());         assertEquals(new Integer(0), i.next());         assertEquals(new Integer(200), i.next());         assertEquals(new Integer(1), i.next());         assertEquals(new Integer(300), i.next());         assertEquals(new Integer(2), i.next());         assertEquals(new Integer(3), i.next());         assertEquals(new Integer(4), i.next());         assertEquals(new Integer(5), i.next());         assertEquals(new Integer(6), i.next());         assertEquals(new Integer(400), i.next());         assertEquals(new Integer(7), i.next());         assertEquals(new Integer(500), i.next());         try {             i.next();             fail();         } catch (Exception e) {             // an exception signifies a successfull test case             assertTrue(true);         }     }     public void testRandomRemoveOnQueue() {         CircularQueue<Integer> q = new CircularQueue<Integer>();         // Create a queue with 5 elements and capacity 8;         for (int i = 0; i < 5; i++) {             q.offer(new Integer(i));         }         q.remove(0);         q.remove(2);         q.remove(2);         Iterator<Integer> i = q.iterator();         assertEquals(2, q.size());         assertEquals(new Integer(1), i.next());         assertEquals(new Integer(2), i.next());         try {             i.next();             fail();         } catch (Exception e) {             // an exception signifies a successfull test case             assertTrue(true);         }     }     public void testRandomRemoveOnRotatedQueue() {         CircularQueue<Integer> q = getRotatedQueue();         q.remove(0); // removeFirst         q.remove(2); // removeLast in the first half         q.remove(2); // removeFirst in the first half         q.remove(4); // removeLast         Iterator<Integer> i = q.iterator();         assertEquals(4, q.size());         assertEquals(new Integer(1), i.next());         assertEquals(new Integer(2), i.next());         assertEquals(new Integer(5), i.next());         assertEquals(new Integer(6), i.next());         try {             i.next();             fail();         } catch (Exception e) {             // an exception signifies a successfull test case             assertTrue(true);                     }     }          public void testExpandAndShrink() throws Exception {         CircularQueue<Integer> q = new CircularQueue<Integer>();         for (int i = 0; i < 1024; i ++) {             q.offer(i);         }                  assertEquals(1024, q.capacity());                  for (int i = 0; i < 512; i ++) {             q.offer(i);             q.poll();         }                  assertEquals(2048, q.capacity());                  for (int i = 0; i < 1024; i ++) {              q.poll();         }                  assertEquals(4, q.capacity());     }     private CircularQueue<Integer> getRotatedQueue() {         CircularQueue<Integer> q = new CircularQueue<Integer>();         // Ensure capacity: 16         for (int i = 0; i < 16; i++) {             q.offer(new Integer(-1));         }         q.clear();         // Rotate it         for (int i = 0; i < 12; i++) {             q.offer(new Integer(-1));             q.poll();         }         // Now push items         for (int i = 0; i < 8; i++) {             q.offer(new Integer(i));         }         return q;     }     public static void main(String[] args) {         junit.textui.TestRunner.run(CircularQueueTest.class);     } }