blob: 5020f0d40a23c1104a8cb2eec95cd02b6d5abc7d [file] [log] [blame]
/*
* 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.
*/
#ifndef _DECAF_UTIL_PRIORITYQUEUE_H_
#define _DECAF_UTIL_PRIORITYQUEUE_H_
#include <decaf/util/Config.h>
#include <decaf/util/Collection.h>
#include <decaf/util/AbstractQueue.h>
#include <decaf/util/Iterator.h>
#include <decaf/util/Comparator.h>
#include <decaf/util/comparators/Less.h>
#include <decaf/lang/Math.h>
#include <decaf/lang/Pointer.h>
#include <decaf/lang/exceptions/NullPointerException.h>
#include <decaf/lang/exceptions/UnsupportedOperationException.h>
#include <memory>
namespace decaf {
namespace util {
class DECAF_API PriorityQueueBase {
protected:
static const int DEFAULT_CAPACITY;
static const int DEFAULT_CAPACITY_RATIO;
virtual ~PriorityQueueBase() {}
};
/**
* An unbounded priority queue based on a binary heap algorithm. The elements of the priority queue
* are ordered according to their natural ordering, or by a Comparator provided to one of the constructors
* that accepts Comparators. A priority queue relying on natural ordering also does not permit insertion
* of non-comparable objects (doing so may result in a compiler error).
*
* The head of this queue is the least element with respect to the specified ordering. If multiple
* elements are tied for least value, the head is one of those elements -- ties are broken arbitrarily.
* The queue retrieval operations poll, remove, peek, and element access the element at the head of
* the queue.
*
* A priority queue is unbounded, but has an internal capacity governing the size of an array used to store
* the elements on the queue. It is always at least as large as the queue size. As elements are added to
* a priority queue, its capacity grows automatically. The details of the growth policy are not specified.
*
* This class and its iterator implement all of the optional methods of the Collection and Iterator
* interfaces. The Iterator provided in method iterator() is not guaranteed to traverse the elements of
* the priority queue in any particular order. If you need ordered traversal, consider using
* <code>Arrays::sort( pq.toArray() )</code>.
*
* Note that this implementation is not synchronized. Multiple threads should not access a PriorityQueue
* instance concurrently if any of the threads modifies the queue. Instead, use the thread-safe
* PriorityBlockingQueue class.
*
* Implementation note: this implementation provides O(log(n)) time for the enqueing and dequeing methods
* (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods;
* and constant time for the retrieval methods (peek, element, and size).
*
* @since 1.0
*/
template< typename E >
class PriorityQueue : public AbstractQueue<E>, private PriorityQueueBase {
private:
int _size;
int capacity;
E* elements;
decaf::lang::Pointer< Comparator<E> > _comparator;
private:
class PriorityQueueIterator : public Iterator<E> {
private:
PriorityQueueIterator(const PriorityQueueIterator&);
PriorityQueueIterator& operator=(const PriorityQueueIterator&);
private:
int position;
bool allowRemove;
PriorityQueue* queue;
public:
PriorityQueueIterator( PriorityQueue* queue ) : position( 0 ), allowRemove( false ), queue( queue ) {}
virtual E next() {
if (!hasNext()) {
throw NoSuchElementException(
__FILE__, __LINE__, "No more elements to Iterate over.");
}
allowRemove = true;
return queue->elements[position++];
}
virtual bool hasNext() const {
return position < queue->_size;
}
virtual void remove() {
if (!allowRemove) {
throw lang::exceptions::IllegalStateException(
__FILE__, __LINE__, "No more elements to Iterate over.");
}
allowRemove = false;
queue->removeAt(position--);
}
};
class ConstPriorityQueueIterator : public PriorityQueueIterator {
private:
ConstPriorityQueueIterator( const ConstPriorityQueueIterator& );
ConstPriorityQueueIterator& operator= ( const ConstPriorityQueueIterator& );
public:
ConstPriorityQueueIterator(const PriorityQueue* queue) :
PriorityQueueIterator(const_cast<PriorityQueue*>(queue)) {}
virtual void remove() {
throw lang::exceptions::UnsupportedOperationException(
__FILE__, __LINE__,
"PriorityQueue::Iterator::remove - Not Valid on a Const Iterator");
}
};
friend class PriorityQueueIterator;
public:
/**
* Creates a Priority Queue with the default initial capacity.
*/
PriorityQueue() : AbstractQueue<E>(), _size(0), capacity(0), elements( NULL), _comparator() {
this->initQueue(DEFAULT_CAPACITY, new comparators::Less<E>());
}
/**
* Creates a Priority Queue with the capacity value supplied.
*
* @param initialCapacity
* The initial number of elements allocated to this PriorityQueue.
*/
PriorityQueue(int initialCapacity) :
AbstractQueue<E>(), _size(0), capacity(0), elements( NULL), _comparator() {
this->initQueue(initialCapacity, new comparators::Less<E>());
}
/**
* Creates a Priority Queue with the default initial capacity. This new PriorityQueue takes
* ownership of the passed Comparator instance and uses that to determine the ordering of the
* elements in the Queue.
*
* @param initialCapacity
* The initial number of elements allocated to this PriorityQueue.
* @param comparator
* The Comparator instance to use in sorting the elements in the Queue.
*
* @throws NullPointerException if the passed Comparator is NULL.
*/
PriorityQueue(int initialCapacity, Comparator<E>* comparator) :
AbstractQueue<E>(), _size(0), capacity(0), elements( NULL), _comparator() {
if (comparator == NULL) {
throw decaf::lang::exceptions::NullPointerException(
__FILE__, __LINE__, "Passed Comparator Cannot be NULL.");
}
this->initQueue(initialCapacity, comparator);
}
/**
* Creates a PriorityQueue containing the elements in the specified Collection.
*
* @param source
* the Collection whose elements are to be placed into this priority queue
*/
PriorityQueue(const Collection<E>& source) :
AbstractQueue<E>(), _size(0), capacity(0), elements( NULL), _comparator() {
this->getFromCollection(source);
}
/**
* Creates a PriorityQueue containing the elements in the specified priority queue. This priority
* queue will be ordered according to the same ordering as the given priority queue.
*
* @param source
* the priority queue whose elements are to be placed into this priority queue
*/
PriorityQueue(const PriorityQueue<E>& source) :
AbstractQueue<E>(), _size(0), capacity(0), elements( NULL), _comparator() {
this->getFromPriorityQueue(source);
}
virtual ~PriorityQueue() {
delete [] elements;
}
/**
* Assignment operator, assign another Collection to this one.
*
* @param source
* The Collection to copy to this one.
*/
PriorityQueue<E>& operator= ( const Collection<E>& source ) {
this->getFromCollection( source );
return *this;
}
/**
* Assignment operator, assign another PriorityQueue to this one.
*
* @param source
* The PriorityQueue to copy to this one.
*/
PriorityQueue<E>& operator= ( const PriorityQueue<E>& source ) {
this->getFromPriorityQueue( source );
return *this;
}
public:
virtual decaf::util::Iterator<E>* iterator() {
return new PriorityQueueIterator(this);
}
virtual decaf::util::Iterator<E>* iterator() const {
return new ConstPriorityQueueIterator(this);
}
virtual int size() const {
return this->_size;
}
virtual void clear() {
// TODO - Provide a more efficient way to clear the array without reallocating it
// we should keep the size it grew to since if reused it could get that big
// again and reallocating all that memory could be to slow.
delete[] this->elements;
this->elements = new E[DEFAULT_CAPACITY];
this->capacity = DEFAULT_CAPACITY;
this->_size = 0;
}
virtual bool offer( const E& value ) {
// TODO - Check for Null and throw exception
increaseCapacity( _size + 1 );
elements[_size++] = value;
upHeap();
return true;
}
virtual bool poll(E& result) {
if (this->isEmpty()) {
return false;
}
result = elements[0];
removeAt(0);
return true;
}
virtual bool peek(E& result) const {
if (this->isEmpty()) {
return false;
}
result = elements[0];
return true;
}
virtual E remove() {
if (!this->isEmpty()) {
E result = elements[0];
removeAt(0);
return result;
}
throw decaf::util::NoSuchElementException(
__FILE__, __LINE__, "Unable to remove specified element from the Queue.");
}
virtual bool remove(const E& value) {
int targetIndex = 0;
for (targetIndex = 0; targetIndex < _size; targetIndex++) {
if (0 == _comparator->compare(value, elements[targetIndex])) {
break;
}
}
if (_size == 0 || _size == targetIndex) {
return false;
}
removeAt(targetIndex);
return true;
}
virtual bool add(const E& value) {
try {
return offer(value);
}
DECAF_CATCH_RETHROW(lang::exceptions::UnsupportedOperationException)
DECAF_CATCH_RETHROW(lang::exceptions::IllegalArgumentException)
DECAF_CATCH_RETHROW(lang::exceptions::IllegalStateException)
DECAF_CATCH_EXCEPTION_CONVERT(lang::exceptions::NullPointerException, lang::exceptions::UnsupportedOperationException)
DECAF_CATCHALL_THROW(lang::exceptions::UnsupportedOperationException)
}
/**
* obtains a Copy of the Pointer instance that this PriorityQueue is using to compare the
* elements in the queue with. The returned value is a copy, the caller cannot change the
* value if the internal Pointer value.
*
* @return a copy of the Comparator Pointer being used by this Queue.
*/
decaf::lang::Pointer<Comparator<E> > comparator() const {
return this->_comparator;
}
private:
void initQueue(int initialSize, Comparator<E>* comparator) {
this->elements = new E[initialSize];
this->capacity = initialSize;
this->_size = 0;
this->_comparator.reset(comparator);
}
void upHeap() {
int current = _size - 1;
int parent = (current - 1) / 2;
while (current != 0 && _comparator->compare(elements[current], elements[parent]) < 0) {
// swap the two
E tmp = elements[current];
elements[current] = elements[parent];
elements[parent] = tmp;
// update parent and current positions.
current = parent;
parent = (current - 1) / 2;
}
}
void downHeap(int pos) {
int current = pos;
int child = 2 * current + 1;
while (child < _size && !this->isEmpty()) {
// compare the children if they exist
if (child + 1 < _size && _comparator->compare(elements[child + 1], elements[child]) < 0) {
child++;
}
// compare selected child with parent
if (_comparator->compare(elements[current], elements[child]) < 0) {
break;
}
// swap the two
E tmp = elements[current];
elements[current] = elements[child];
elements[child] = tmp;
// update child and current positions
current = child;
child = 2 * current + 1;
}
}
void getFromPriorityQueue(const PriorityQueue<E>& c) {
initCapacity(c);
_comparator = c.comparator();
for (int ix = 0; ix < c.size(); ++ix) {
this->elements[ix] = c.elements[ix];
}
_size = c.size();
}
void getFromCollection(const Collection<E>& c) {
initCapacity(c);
_comparator.reset(new comparators::Less<E>());
std::auto_ptr<Iterator<E> > iter(c.iterator());
while (iter->hasNext()) {
this->offer(iter->next());
}
}
void removeAt(int index) {
_size--;
elements[index] = elements[_size];
downHeap(index);
elements[_size] = E();
}
void initCapacity(const Collection<E>& c) {
delete[] elements;
_size = 0;
if (c.isEmpty()) {
capacity = 1;
elements = new E[capacity];
} else {
capacity = (int) lang::Math::ceil((double) c.size() * 1.1);
elements = new E[capacity];
}
}
void increaseCapacity(int size) {
if (size > capacity) {
E* newElements = new E[size * DEFAULT_CAPACITY_RATIO];
for (int ix = 0; ix < capacity; ix++) {
newElements[ix] = elements[ix];
}
delete[] elements;
elements = newElements;
capacity = size * DEFAULT_CAPACITY_RATIO;
}
}
};
}}
#endif /* _DECAF_UTIL_PRIORITYQUEUE_H_ */