Last active
March 7, 2018 10:45
-
-
Save multiplemonomials/433535933dba6a228f88241d000f4a6e to your computer and use it in GitHub Desktop.
Java collection that implements a fixed-length buffer where adding a new element deletes the oldest element. Uses a fixed array internally, so insertion and access are O(1), and do not cause any memory allocations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package frctest.gui; | |
/* | |
* | |
* Circular queue with a fixed size. | |
* Supports random access. | |
* The first element enqueued has index zero, and enqueueing another element will increment existing elements' indexes by 1. | |
* Enqueue()ing more than maxSize elements will overwrite the highest-index element. | |
* | |
* This class copyright (c) Jamie Smith | |
*/ | |
public class RandomAccessBuffer <T> | |
{ | |
private Object[] elements; | |
/** | |
* current "zero" index | |
*/ | |
private int currentStartIndex; | |
private int size = 0; | |
/** | |
* Get the actual size of the buffer (how many elements have been enqueued). | |
* @return | |
*/ | |
public int getSize() | |
{ | |
return size; | |
} | |
/** | |
* Get the last valid index in the buffer. Same as getMaxSize() - 1. | |
* @return The highest index that can be used in the buffer. -1 if the max size is 0. | |
*/ | |
public int getLastIndex() | |
{ | |
return elements.length - 1; | |
} | |
/** | |
* Get the maximum number of elements that can be inserted before old elements get overwritten. | |
* @return | |
*/ | |
public int getMaxSize() | |
{ | |
return elements.length; | |
} | |
public RandomAccessBuffer(int size) | |
{ | |
elements = new Object[size]; | |
currentStartIndex = size == 0 ? 0 : size - 1; | |
} | |
/** | |
* Add an element at the start. | |
* | |
* If the buffer is full, this shifts it backward. | |
* The element at the end will be removed, all other elements will have their indexes increased by 1, and this element will be added at index 0. | |
* @param element | |
*/ | |
public void enqueue(T element) | |
{ | |
if (elements.length == 0) | |
{ | |
throw new IllegalArgumentException("Tried to add element \"" + element.toString() + "\" to 0 length buffer!"); | |
} | |
if(size < elements.length) | |
{ | |
++size; | |
} | |
//decrement start index | |
if(currentStartIndex == 0) | |
{ | |
currentStartIndex = elements.length - 1; | |
} | |
else | |
{ | |
--currentStartIndex; | |
} | |
elements[currentStartIndex] = element; | |
} | |
/** | |
* Calculate the index in the internal array of the element which appears to be at apparentIndex. | |
* Does validation, and throws an exception if index is invalid | |
*/ | |
private int calcInternalIndex(int apparentIndex) | |
{ | |
if (apparentIndex >= size || apparentIndex < 0) | |
{ | |
throw new ArrayIndexOutOfBoundsException("Index: " + apparentIndex + ", size: " + size); | |
} | |
int internalIndex = apparentIndex + currentStartIndex; | |
if (internalIndex >= elements.length) | |
{ | |
internalIndex -= elements.length - 1; | |
} | |
return internalIndex; | |
} | |
/** | |
* Get the element at index. | |
* @param index | |
* @return | |
*/ | |
@SuppressWarnings("unchecked") | |
public T get(int index) | |
{ | |
return (T) elements[calcInternalIndex(index)]; | |
} | |
/** | |
* Set the element at index to value. | |
* | |
* Does not shift the buffer, use enqueue() for that. | |
* | |
* If index >= size and index < maxSize, then size will be increased to index + 1, and all of the elements in between will be filled with nulls. | |
* @param index | |
* @param value | |
*/ | |
public void set(int index, T value) | |
{ | |
if(index >= size && index < getMaxSize()) | |
{ | |
size = index + 1; | |
} | |
elements[calcInternalIndex(index)] = value; | |
} | |
//Changes the fixed size of the queue | |
//Extra space is added at, and surplus elements are removed from, the end. | |
public void resize(int newSize) | |
{ | |
Object[] newBackingArray = new Object[newSize]; | |
//we'll use apparent indexes for this, it's too hard to use real indexes | |
for(int index = 0; index < Math.min(size, newSize); ++index) | |
{ | |
//note: on the new array, since the start index is 0, apparent indexes and real indexes are the same | |
newBackingArray[index] = get(index); | |
} | |
//now use the new array | |
currentStartIndex = 0; | |
elements = newBackingArray; | |
size = newSize; | |
} | |
@Override | |
public String toString() | |
{ | |
StringBuilder retval = new StringBuilder(); | |
retval.append('{'); | |
//we'll use apparent indexes for this, it's too hard to use real indexes | |
for (int index = 0; index < size; ++index) | |
{ | |
retval.append('['); | |
retval.append(String.valueOf(get(index))); | |
retval.append(']'); | |
if(index < size - 1) | |
{ | |
retval.append(", "); | |
} | |
} | |
retval.append('}'); | |
// | |
// retval.append(" iind: "); | |
// retval.append(currentStartIndex); | |
// retval.append(Arrays.toString(elements)); | |
return retval.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment