Mega Code Archive

 
Categories / Java / Collections Data Structure
 

A circular queue from mina

//package com.google.code.yanf4j.util; /*  *  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 circular queue from mina  *   * @author dennis  *   * @param <E>  */ public class CircularQueue<E> extends AbstractList<E> implements List<E>,     Queue<E>, Serializable {   private static final long serialVersionUID = 3993421269224511264L;   private static final int DEFAULT_CAPACITY = 4;   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);     this.items = new Object[actualCapacity];     this.mask = actualCapacity - 1;     this.initialCapacity = actualCapacity;     this.shrinkThreshold = 0;   }   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 this.items.length;   }   @Override   public void clear() {     if (!isEmpty()) {       Arrays.fill(this.items, null);       this.first = 0;       this.last = 0;       this.full = false;       shrinkIfNeeded();     }   }   @SuppressWarnings("unchecked")   public E poll() {     if (isEmpty()) {       return null;     }     Object ret = this.items[this.first];     this.items[this.first] = null;     decreaseSize();     if (this.first == this.last) {       this.first = this.last = 0;     }     shrinkIfNeeded();     return (E) ret;   }   public boolean offer(E item) {     if (item == null) {       throw new NullPointerException("item");     }     expandIfNeeded();     this.items[this.last] = item;     increaseSize();     return true;   }   @SuppressWarnings("unchecked")   public E peek() {     if (isEmpty()) {       return null;     }     return (E) this.items[this.first];   }   @SuppressWarnings("unchecked")   @Override   public E get(int idx) {     checkIndex(idx);     return (E) this.items[getRealIndex(idx)];   }   @Override   public boolean isEmpty() {     return (this.first == this.last) && !this.full;   }   @Override   public int size() {     if (this.full) {       return capacity();     }     if (this.last >= this.first) {       return this.last - this.first;     } else {       return this.last - this.first + capacity();     }   }   @Override   public String toString() {     return "first=" + this.first + ", last=" + this.last + ", size="         + size() + ", mask = " + this.mask;   }   private void checkIndex(int idx) {     if (idx < 0 || idx >= size()) {       throw new IndexOutOfBoundsException(String.valueOf(idx));     }   }   private int getRealIndex(int idx) {     return (this.first + idx) & this.mask;   }   private void increaseSize() {     this.last = (this.last + 1) & this.mask;     this.full = this.first == this.last;   }   private void decreaseSize() {     this.first = (this.first + 1) & this.mask;     this.full = false;   }   private void expandIfNeeded() {     if (this.full) {       // expand queue       final int oldLen = this.items.length;       final int newLen = oldLen << 1;       Object[] tmp = new Object[newLen];       if (this.first < this.last) {         System.arraycopy(this.items, this.first, tmp, 0, this.last             - this.first);       } else {         System.arraycopy(this.items, this.first, tmp, 0, oldLen             - this.first);         System.arraycopy(this.items, 0, tmp, oldLen - this.first,             this.last);       }       this.first = 0;       this.last = oldLen;       this.items = tmp;       this.mask = tmp.length - 1;       if (newLen >>> 3 > this.initialCapacity) {         this.shrinkThreshold = newLen >>> 3;       }     }   }   private void shrinkIfNeeded() {     int size = size();     if (size <= this.shrinkThreshold) {       // shrink queue       final int oldLen = this.items.length;       int newLen = normalizeCapacity(size);       if (size == newLen) {         newLen <<= 1;       }       if (newLen >= oldLen) {         return;       }       if (newLen < this.initialCapacity) {         if (oldLen == this.initialCapacity) {           return;         } else {           newLen = this.initialCapacity;         }       }       Object[] tmp = new Object[newLen];       // Copy only when there's something to copy.       if (size > 0) {         if (this.first < this.last) {           System.arraycopy(this.items, this.first, tmp, 0, this.last               - this.first);         } else {           System.arraycopy(this.items, this.first, tmp, 0, oldLen               - this.first);           System.arraycopy(this.items, 0, tmp, oldLen - this.first,               this.last);         }       }       this.first = 0;       this.last = size;       this.items = tmp;       this.mask = tmp.length - 1;       this.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 = this.items[realIdx];     this.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 (this.first < this.last) {       System.arraycopy(this.items, realIdx, this.items, realIdx + 1,           this.last - realIdx);     } else {       if (realIdx >= this.first) {         System.arraycopy(this.items, 0, this.items, 1, this.last);         this.items[0] = this.items[this.items.length - 1];         System.arraycopy(this.items, realIdx, this.items, realIdx + 1,             this.items.length - realIdx - 1);       } else {         System.arraycopy(this.items, realIdx, this.items, realIdx + 1,             this.last - realIdx);       }     }     this.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 = this.items[realIdx];     // Remove a room for the removed element.     if (this.first < this.last) {       System.arraycopy(this.items, this.first, this.items,           this.first + 1, realIdx - this.first);     } else {       if (realIdx >= this.first) {         System.arraycopy(this.items, this.first, this.items,             this.first + 1, realIdx - this.first);       } else {         System.arraycopy(this.items, 0, this.items, 1, realIdx);         this.items[0] = this.items[this.items.length - 1];         System.arraycopy(this.items, this.first, this.items,             this.first + 1, this.items.length - this.first - 1);       }     }     this.items[this.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();   } }