Queue implementation using a rotating, resizable array for fast random access
This commit is contained in:
parent
6e5e560e33
commit
a6c49f138e
1 changed files with 382 additions and 0 deletions
382
tools/cooja/java/se/sics/cooja/util/ArrayQueue.java
Normal file
382
tools/cooja/java/se/sics/cooja/util/ArrayQueue.java
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* Copyright (c) 2010, BotBox AB. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: ArrayQueue.java,v 1.1 2010/09/09 19:51:53 nifi Exp $
|
||||
*/
|
||||
|
||||
package se.sics.cooja.util;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
* The ArrayQueue class implements a simple queue using a rotating, resizable
|
||||
* array. Permits all elements, including <tt>null</tt>.
|
||||
*
|
||||
* The usage is basically the same as for ArrayList but this implementation
|
||||
* is optimized for adding last and removing first element while the ArrayList
|
||||
* shifts the data when removing first element.
|
||||
*
|
||||
* <p><strong>
|
||||
* Note that this implementation is not synchronized and if an ArrayQueue
|
||||
* instance is accessed by several threads concurrently, and at least one
|
||||
* thread modifies the queue, it must be synchronized externally.
|
||||
* </strong>
|
||||
*
|
||||
* @author Joakim Eriksson (joakime@sics.se)
|
||||
* @author Niclas Finne (nfi@sics.se)
|
||||
*/
|
||||
public class ArrayQueue<E> extends AbstractList<E> implements RandomAccess, Cloneable, java.io.Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5791745982858131414L;
|
||||
|
||||
private transient E[] queueData;
|
||||
private transient int first = 0;
|
||||
private transient int last = 0;
|
||||
private int size = 0;
|
||||
|
||||
public ArrayQueue() {
|
||||
this(16);
|
||||
}
|
||||
|
||||
public ArrayQueue(int initialCapacity) {
|
||||
if (initialCapacity < 0) {
|
||||
throw new IllegalArgumentException("illegal capacity: " +
|
||||
initialCapacity);
|
||||
}
|
||||
queueData = allocate(initialCapacity);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private E[] allocate(int size) {
|
||||
return (E[]) new Object[size];
|
||||
}
|
||||
|
||||
public void ensureCapacity(int minCapacity) {
|
||||
int capacity = queueData.length;
|
||||
if (capacity < minCapacity) {
|
||||
// int newCapacity = (capacity * 3) / 2 + 1;
|
||||
int newCapacity = capacity * 2;
|
||||
set(newCapacity < minCapacity ? minCapacity : newCapacity);
|
||||
}
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all data in the queue to a new data array of the specified
|
||||
* size (MUST be large enough) and replaces the old data array.
|
||||
*/
|
||||
private void set(int newCapacity) {
|
||||
E[] newData = allocate(newCapacity);
|
||||
copy(newData);
|
||||
// The data is always in the beginning after allocating new data array
|
||||
first = 0;
|
||||
last = size;
|
||||
queueData = newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all data in the queue to the new data array (must be large enough!)
|
||||
*/
|
||||
private void copy(Object[] newData) {
|
||||
if (first < last) {
|
||||
// No wrap ([...1,2,3,4...])
|
||||
System.arraycopy(queueData, first, newData, 0, size);
|
||||
} else if (size > 0) {
|
||||
// Wrapped queue ([4,5,6,......,1,2,3])
|
||||
int firstSize = queueData.length - first;
|
||||
// At least one element must exist since size > 0
|
||||
System.arraycopy(queueData, first, newData, 0, firstSize);
|
||||
System.arraycopy(queueData, 0, newData, firstSize, last);
|
||||
} else {
|
||||
// Empty queue i.e. do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public void trimToSize() {
|
||||
if (size < queueData.length) {
|
||||
// Make sure only the needed space is occupied
|
||||
set(size);
|
||||
modCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
public boolean contains(Object element) {
|
||||
return indexOf(element) >= 0;
|
||||
}
|
||||
|
||||
public int indexOf(Object element) {
|
||||
return indexOf(element, 0);
|
||||
}
|
||||
|
||||
public int indexOf(Object element, int index) {
|
||||
if ((index < 0) || (index >= size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
final int capacity = queueData.length;
|
||||
if (element == null) {
|
||||
for(; index < size; index++) {
|
||||
if (queueData[(first + index) % capacity] == null) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for(; index < size; index++) {
|
||||
if (element.equals(queueData[(first + index) % capacity])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int lastIndexOf(Object element) {
|
||||
final int capacity = queueData.length;
|
||||
if (element == null) {
|
||||
for(int index = size - 1; index >= 0; index--) {
|
||||
if (queueData[(first + index) % capacity] == null) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int index = size - 1; index >= 0; index--) {
|
||||
if (element.equals(queueData[(first + index) % capacity])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public E get(int index) {
|
||||
// getPos() will ensure that the index is valid
|
||||
return queueData[getPos(index)];
|
||||
}
|
||||
|
||||
public E set(int index, E element) {
|
||||
int pos = getPos(index);
|
||||
E oldValue = queueData[pos];
|
||||
queueData[pos] = element;
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
public void add(int index, E element) {
|
||||
// Make sure there is space for the new element.
|
||||
ensureCapacity(size + 1);
|
||||
if (index == size) {
|
||||
// Add to the end of the queue
|
||||
queueData[last] = element;
|
||||
last = (last + 1) % queueData.length;
|
||||
size++;
|
||||
} else {
|
||||
// This will make sure the index is valid (0 <= index < size)
|
||||
index = getPos(index);
|
||||
|
||||
if (index == first) {
|
||||
// Add to the beginning of the queue
|
||||
if (first > 0) {
|
||||
first--;
|
||||
} else {
|
||||
// first == 0 => move first to the end of the data array
|
||||
first = queueData.length - 1;
|
||||
}
|
||||
index = first;
|
||||
} else if (index < last) {
|
||||
// Non wrapped queue or index is in the lower part of the data array
|
||||
System.arraycopy(queueData, index, queueData, index + 1, last - index);
|
||||
last = (last + 1) % queueData.length;
|
||||
} else {
|
||||
// Wrapped queue (index in the end of the data array)
|
||||
// first > 0 because last < first (there is at least one free position)
|
||||
System.arraycopy(queueData, first, queueData, first - 1,
|
||||
index - first);
|
||||
index--;
|
||||
first--;
|
||||
}
|
||||
queueData[index] = element;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
public E remove(int index) {
|
||||
E value;
|
||||
// This method also checks that the index is valid
|
||||
index = getPos(index);
|
||||
|
||||
value = queueData[index];
|
||||
if (index == first) {
|
||||
// Remove first element. The most common case for a queue.
|
||||
queueData[first] = null;
|
||||
first = (first + 1) % queueData.length;
|
||||
|
||||
} else if (index < last) {
|
||||
// Non wrapped queue or index is in the lower part of the data array.
|
||||
last--;
|
||||
if (index < last) {
|
||||
// An element in the middle is removed
|
||||
System.arraycopy(queueData, index + 1, queueData, index, last - index);
|
||||
}
|
||||
queueData[last] = null;
|
||||
|
||||
} else if ((last == 0) && (index == queueData.length - 1)) {
|
||||
// Minor optimization: no elements at the beginning of the data
|
||||
// array and the removed index is last in the data array.
|
||||
queueData[index] = null;
|
||||
last = queueData.length - 1;
|
||||
|
||||
} else {
|
||||
// Wrapped queue (index in the end of the data array)
|
||||
System.arraycopy(queueData, first, queueData, first + 1, index - first);
|
||||
queueData[first++] = null;
|
||||
}
|
||||
size--;
|
||||
modCount++;
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean remove(Object element) {
|
||||
int index = indexOf(element);
|
||||
if (index >= 0) {
|
||||
remove(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
final int capacity = queueData.length;
|
||||
for (int i = 0; i < size; i++) {
|
||||
queueData[(first + i) % capacity] = null;
|
||||
}
|
||||
first = last = size = 0;
|
||||
modCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a shallow copy of this queue (the elements themselves are not
|
||||
* copied).
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrayQueue<E> clone() {
|
||||
try {
|
||||
ArrayQueue<E> v = (ArrayQueue<E>) super.clone();
|
||||
// The set method will always create a new array which gives the
|
||||
// cloned queue its own copy of the data array.
|
||||
v.set(v.size);
|
||||
v.modCount = 0;
|
||||
return v;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// Should never happen
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
Object[] array = new Object[size];
|
||||
copy(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] array) {
|
||||
if (array.length < size) {
|
||||
array = (T[]) java.lang.reflect
|
||||
.Array.newInstance(array.getClass().getComponentType(), size);
|
||||
}
|
||||
copy(array);
|
||||
if (array.length > size) {
|
||||
array[size] = null;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private int getPos(int index) {
|
||||
if ((index >= 0) && (index < size)) {
|
||||
return (first + index) % queueData.length;
|
||||
}
|
||||
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getName()).append('[')
|
||||
.append(first).append(',').append(last)
|
||||
.append(',').append(size)
|
||||
.append(",[");
|
||||
if (queueData.length < 32) {
|
||||
for (int i = 0, n = queueData.length; i < n; i++) {
|
||||
if (i > 0) sb.append(',');
|
||||
sb.append(queueData[i]);
|
||||
}
|
||||
} else {
|
||||
sb.append("...");
|
||||
}
|
||||
sb.append("]]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Serialization
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
private void writeObject(java.io.ObjectOutputStream out)
|
||||
throws java.io.IOException
|
||||
{
|
||||
int queueLen = queueData.length;
|
||||
out.defaultWriteObject();
|
||||
out.writeInt(queueLen);
|
||||
for (int i = 0, index = first; i < size; i++) {
|
||||
out.writeObject(queueData[index]);
|
||||
index = (index + 1) % queueLen;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readObject(java.io.ObjectInputStream in)
|
||||
throws java.io.IOException, ClassNotFoundException
|
||||
{
|
||||
in.defaultReadObject();
|
||||
queueData = (E[]) new Object[in.readInt()];
|
||||
for (int i = 0; i < size; i++) {
|
||||
queueData[i] = (E) in.readObject();
|
||||
}
|
||||
first = 0;
|
||||
last = size;
|
||||
}
|
||||
|
||||
} // ArrayQueue
|
Loading…
Reference in a new issue