437 lines
12 KiB
Plaintext
437 lines
12 KiB
Plaintext
package speiger.src.collections.PACKAGE.queues;
|
|
|
|
import java.util.Arrays;
|
|
#if TYPE_OBJECT
|
|
import java.util.Comparator;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.BiFunction;
|
|
#endif
|
|
import java.util.Objects;
|
|
import java.util.NoSuchElementException;
|
|
|
|
import speiger.src.collections.PACKAGE.collections.ITERATOR;
|
|
#if !TYPE_OBJECT
|
|
import speiger.src.collections.PACKAGE.functions.COMPARATOR;
|
|
import speiger.src.collections.PACKAGE.functions.CONSUMER;
|
|
#endif
|
|
import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER;
|
|
import speiger.src.collections.PACKAGE.functions.function.PREDICATE;
|
|
import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR;
|
|
import speiger.src.collections.utils.ITrimmable;
|
|
|
|
/**
|
|
* A Simple First In First Out Priority Queue that is a Good Replacement for a linked list (or ArrayDequeue)
|
|
* Its specific implementation uses a backing array that grows and shrinks as it is needed.
|
|
* @Type(T)
|
|
*/
|
|
public class ARRAY_FIFO_QUEUE KEY_GENERIC_TYPE extends ABSTRACT_PRIORITY_QUEUE KEY_GENERIC_TYPE implements PRIORITY_DEQUEUE KEY_GENERIC_TYPE, ITrimmable
|
|
{
|
|
/** Max Possible ArraySize without the JVM Crashing */
|
|
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
|
/** The Minimum Capacity that is allowed */
|
|
public static final int MIN_CAPACITY = 4;
|
|
/** The Backing array */
|
|
protected transient KEY_TYPE[] array;
|
|
/** The First Index pointer */
|
|
protected int first;
|
|
/** The Last Index pointer */
|
|
protected int last;
|
|
/** The Minimum Capacity of the Queue **/
|
|
protected int minCapacity;
|
|
|
|
/**
|
|
* Constructor using a initial array
|
|
* @param values the Array that should be used
|
|
*/
|
|
public ARRAY_FIFO_QUEUE(KEY_TYPE[] values) {
|
|
this(values, 0, values.length);
|
|
}
|
|
|
|
/**
|
|
* Constructor using a initial array
|
|
* @param values the Array that should be used
|
|
* @param size the amount of elements that are in the initial array
|
|
* @throws IllegalStateException if values is smaller then size
|
|
*/
|
|
public ARRAY_FIFO_QUEUE(KEY_TYPE[] values, int size) {
|
|
this(values, 0, size);
|
|
}
|
|
|
|
/**
|
|
* Constructor using a initial array
|
|
* @param values the Array that should be used
|
|
* @param offset where to begin in the initial array
|
|
* @param size the amount of elements that are in the initial array
|
|
* @throws IllegalStateException if values is smaller then size
|
|
*/
|
|
public ARRAY_FIFO_QUEUE(KEY_TYPE[] values, int offset, int size) {
|
|
if (values.length < size) throw new IllegalArgumentException("Initial array (" + values.length + ") is smaller then the expected size (" + size + ")");
|
|
if(values.length <= 0) values = NEW_KEY_ARRAY(MIN_CAPACITY);
|
|
else if(values.length < MIN_CAPACITY) values = Arrays.copyOf(values, MIN_CAPACITY);
|
|
minCapacity = MIN_CAPACITY;
|
|
array = values;
|
|
first = offset;
|
|
last = (offset + size) % array.length;
|
|
if(array.length == size) expand();
|
|
}
|
|
|
|
/**
|
|
* Constructor with a Min Capacity
|
|
* @param capacity the initial capacity of the backing array
|
|
* @throws IllegalStateException if the initial size is smaller 0
|
|
*/
|
|
public ARRAY_FIFO_QUEUE(int capacity) {
|
|
if (capacity < 0) throw new IllegalArgumentException("Initial capacity (" + capacity + ") is negative");
|
|
array = NEW_KEY_ARRAY(Math.max(MIN_CAPACITY, capacity));
|
|
minCapacity = array.length;
|
|
}
|
|
|
|
/**
|
|
* Default Construtor
|
|
*/
|
|
public ARRAY_FIFO_QUEUE() {
|
|
this(MIN_CAPACITY);
|
|
}
|
|
|
|
@Override
|
|
public ITERATOR KEY_GENERIC_TYPE iterator() {
|
|
return new Iter();
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
final int apparentLength = last - first;
|
|
return apparentLength >= 0 ? apparentLength : array.length + apparentLength;
|
|
}
|
|
|
|
@Override
|
|
public void clear() {
|
|
#if TYPE_OBJECT
|
|
Arrays.fill(array, null);
|
|
#endif
|
|
first = last = 0;
|
|
}
|
|
|
|
@Override
|
|
public void enqueue(KEY_TYPE e) {
|
|
array[last] = e;
|
|
last = ++last % array.length;
|
|
if(last == first) expand();
|
|
}
|
|
|
|
@Override
|
|
public void enqueueFirst(KEY_TYPE e) {
|
|
if(first == 0) first = array.length;
|
|
array[--first] = e;
|
|
if(first == last) expand();
|
|
}
|
|
|
|
@Override
|
|
public KEY_TYPE dequeue() {
|
|
if(first == last) throw new NoSuchElementException();
|
|
KEY_TYPE data = array[first];
|
|
#if TYPE_OBJECT
|
|
array[first] = null;
|
|
#endif
|
|
first = ++first % array.length;
|
|
reduce();
|
|
return data;
|
|
}
|
|
|
|
@Override
|
|
public KEY_TYPE dequeueLast() {
|
|
if(first == last) throw new NoSuchElementException();
|
|
if(last == 0) last = array.length;
|
|
KEY_TYPE data = array[--last];
|
|
#if TYPE_OBJECT
|
|
array[last] = null;
|
|
#endif
|
|
reduce();
|
|
return data;
|
|
}
|
|
|
|
@Override
|
|
public KEY_TYPE peek(int index) {
|
|
if(first == last || index < 0 || index > size()) throw new NoSuchElementException();
|
|
return array[(first + index) % array.length];
|
|
}
|
|
|
|
@Override
|
|
public boolean removeFirst(KEY_TYPE e) {
|
|
if(first == last) return false;
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
int index = (first + i) % array.length;
|
|
if(e == array[index])
|
|
return removeIndex(index);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean removeLast(KEY_TYPE e) {
|
|
if(first == last) return false;
|
|
for(int i = size()-1;i>=0;i--) {
|
|
int index = (first + i) % array.length;
|
|
if(e == array[index])
|
|
return removeIndex(index);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected boolean removeIndex(int index) {
|
|
if(first >= last ? index < first && index > last : index < first || index > last) return false;
|
|
if(index == first) {
|
|
#if TYPE_OBJECT
|
|
array[first] = null;
|
|
#endif
|
|
first++;
|
|
}
|
|
else if(index == last) {
|
|
last--;
|
|
#if TYPE_OBJECT
|
|
array[last] = null;
|
|
#endif
|
|
}
|
|
else if(index > last) {
|
|
System.arraycopy(array, first, array, first+1, (index - first));
|
|
#if TYPE_OBJECT
|
|
array[first] = null;
|
|
#endif
|
|
first = ++first % array.length;
|
|
}
|
|
else if(index < first) {
|
|
System.arraycopy(array, index+1, array, index, (last - index) - 1);
|
|
#if TYPE_OBJECT
|
|
array[last] = null;
|
|
#endif
|
|
if(--last < 0) last += array.length;
|
|
}
|
|
else {
|
|
if(index - first < last - index) {
|
|
System.arraycopy(array, first, array, first+1, (index - first));
|
|
#if TYPE_OBJECT
|
|
array[first] = null;
|
|
#endif
|
|
first = ++first % array.length;
|
|
}
|
|
else {
|
|
System.arraycopy(array, index+1, array, index, (last - index) - 1);
|
|
#if TYPE_OBJECT
|
|
array[last] = null;
|
|
#endif
|
|
if(--last < 0) last += array.length;
|
|
}
|
|
}
|
|
reduce();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onChanged() {}
|
|
|
|
@Override
|
|
public ARRAY_FIFO_QUEUE KEY_GENERIC_TYPE copy() {
|
|
ARRAY_FIFO_QUEUE KEY_GENERIC_TYPE queue = new ARRAY_FIFO_QUEUEBRACES();
|
|
queue.first = first;
|
|
queue.last = last;
|
|
queue.minCapacity = minCapacity;
|
|
queue.array = Arrays.copyOf(array, array.length);
|
|
return queue;
|
|
}
|
|
|
|
@Override
|
|
public COMPARATOR KEY_SUPER_GENERIC_TYPE comparator() { return null; }
|
|
|
|
@Override
|
|
public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) {
|
|
Objects.requireNonNull(action);
|
|
if(first == last) return;
|
|
for(int i = 0,m=size();i<m;i++)
|
|
action.accept(array[(first + i) % array.length]);
|
|
clearAndTrim(0);
|
|
}
|
|
|
|
@Override
|
|
public <E> void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE<E> action) {
|
|
Objects.requireNonNull(action);
|
|
if(first == last) return;
|
|
for(int i = 0,m=size();i<m;i++)
|
|
action.accept(input, array[(first + i) % array.length]);
|
|
clearAndTrim(0);
|
|
}
|
|
|
|
@Override
|
|
public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) {
|
|
Objects.requireNonNull(filter);
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
if(filter.TEST_VALUE(array[(first + i) % array.length])) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) {
|
|
Objects.requireNonNull(filter);
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
if(filter.TEST_VALUE(array[(first + i) % array.length])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) {
|
|
Objects.requireNonNull(filter);
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
if(!filter.TEST_VALUE(array[(first + i) % array.length])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) {
|
|
Objects.requireNonNull(filter);
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
int index = (first + i) % array.length;
|
|
if(filter.TEST_VALUE(array[index])) {
|
|
KEY_TYPE data = array[index];
|
|
removeIndex(index);
|
|
return data;
|
|
}
|
|
}
|
|
return EMPTY_VALUE;
|
|
}
|
|
|
|
#if !TYPE_OBJECT
|
|
@Override
|
|
public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) {
|
|
Objects.requireNonNull(operator);
|
|
KEY_TYPE state = identity;
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
state = operator.APPLY_VALUE(state, array[(first + i) % array.length]);
|
|
}
|
|
return state;
|
|
}
|
|
|
|
#else
|
|
@Override
|
|
public <KEY_SPECIAL_TYPE> KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction<KEY_SPECIAL_TYPE, KEY_TYPE, KEY_SPECIAL_TYPE> operator) {
|
|
Objects.requireNonNull(operator);
|
|
KEY_SPECIAL_TYPE state = identity;
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
state = operator.APPLY_VALUE(state, array[(first + i) % array.length]);
|
|
}
|
|
return state;
|
|
}
|
|
|
|
#endif
|
|
@Override
|
|
public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) {
|
|
Objects.requireNonNull(operator);
|
|
KEY_TYPE state = EMPTY_VALUE;
|
|
boolean empty = true;
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
if(empty) {
|
|
empty = false;
|
|
state = array[(first + i) % array.length];
|
|
continue;
|
|
}
|
|
state = operator.APPLY_VALUE(state, array[(first + i) % array.length]);
|
|
}
|
|
return state;
|
|
}
|
|
|
|
@Override
|
|
public int count(PREDICATE KEY_GENERIC_TYPE filter) {
|
|
Objects.requireNonNull(filter);
|
|
int result = 0;
|
|
for(int i = 0,m=size();i<m;i++) {
|
|
if(filter.TEST_VALUE(array[(first + i) % array.length])) result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean trim(int size) {
|
|
int newSize = Math.max(Math.max(size, size()), minCapacity);
|
|
if(newSize >= array.length) return false;
|
|
KEY_TYPE[] newArray = NEW_KEY_ARRAY(newSize);
|
|
if(first <= last) System.arraycopy(array, first, newArray, 0, last - first);
|
|
else {
|
|
System.arraycopy(array, first, newArray, 0, array.length - first);
|
|
System.arraycopy(array, 0, newArray, array.length - first, last);
|
|
}
|
|
first = 0;
|
|
last = size();
|
|
array = newArray;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Trims the collection down to the requested size and clears all elements while doing so
|
|
* @param size the amount of elements that should be allowed
|
|
* @note this will enforce minimum size of the collection itself
|
|
*/
|
|
@Override
|
|
public void clearAndTrim(int size) {
|
|
int newSize = Math.max(minCapacity, size);
|
|
if(array.length <= newSize) {
|
|
clear();
|
|
return;
|
|
}
|
|
first = last = 0;
|
|
array = NEW_KEY_ARRAY(newSize);
|
|
}
|
|
|
|
@Override
|
|
public GENERIC_SPECIAL_KEY_BRACES<E> KEY_SPECIAL_TYPE[] TO_ARRAY(KEY_SPECIAL_TYPE[] input) {
|
|
if(input == null || input.length < size()) input = NEW_SPECIAL_KEY_ARRAY(size());
|
|
if (first <= last) System.arraycopy(array, first, input, 0, last - first);
|
|
else {
|
|
System.arraycopy(array, first, input, 0, array.length - first);
|
|
System.arraycopy(array, 0, input, array.length - first, last);
|
|
}
|
|
return input;
|
|
}
|
|
|
|
protected void reduce() {
|
|
final int size = size();
|
|
if (array.length > minCapacity && size <= array.length / 4) resize(size, Math.max(array.length / 2, minCapacity));
|
|
}
|
|
|
|
protected void expand() {
|
|
resize(array.length, (int)Math.min(MAX_ARRAY_SIZE, 2L * array.length));
|
|
}
|
|
|
|
protected final void resize(int oldSize, int newSize) {
|
|
KEY_TYPE[] newArray = NEW_KEY_ARRAY(newSize);
|
|
if(first >= last) {
|
|
if(oldSize != 0)
|
|
{
|
|
System.arraycopy(array, first, newArray, 0, array.length - first);
|
|
System.arraycopy(array, 0, newArray, array.length - first, last);
|
|
}
|
|
}
|
|
else System.arraycopy(array, first, newArray, 0, last-first);
|
|
first = 0;
|
|
last = oldSize;
|
|
array = newArray;
|
|
}
|
|
|
|
private class Iter implements ITERATOR KEY_GENERIC_TYPE
|
|
{
|
|
int index = first;
|
|
@Override
|
|
public boolean hasNext()
|
|
{
|
|
return index != last;
|
|
}
|
|
|
|
@Override
|
|
public KEY_TYPE NEXT() {
|
|
KEY_TYPE value = array[index];
|
|
removeIndex(index);
|
|
index = ++index % array.length;
|
|
return value;
|
|
}
|
|
}
|
|
} |