#pragma once //------------------------------------------------------------------------------ /** @class Util::HashTable Organizes key/value pairs by a hash code. Looks very similar to a Dictionary, but may provide better search times (up to O(1)) by computing a (ideally unique) hash code on the key and using that as an index into an array. The flipside is that the key class must provide a hash code and the memory footprint may be larger then Dictionary. The default capacity is 128. Matching the capacity against the number of expected elements in the hash table is one key to get optimal insertion and search times, the other is to provide a good (and fast) hash code computation which produces as few collissions as possible for the key type. The key class must implement the following method in order to work with the HashTable: IndexT HashCode() const; The Util::String class implements this method as an example. Internally the hash table is implemented as a fixed array of sorted arrays. The fixed array is indexed by the hash code of the key, the sorted arrays contain all values with identical keys. (C) 2006 Radon Labs GmbH */ #include "util/fixedarray.h" #include "util/array.h" #include "util/keyvaluepair.h" //------------------------------------------------------------------------------ namespace Util { template class HashTable { public: /// default constructor HashTable(); /// constructor with capacity HashTable(SizeT capacity); /// copy constructor HashTable(const HashTable& rhs); /// assignment operator void operator=(const HashTable& rhs); /// read/write [] operator, assertion if key not found VALUETYPE& operator[](const KEYTYPE& key) const; /// return current number of values in the hashtable SizeT Size() const; /// return fixed-size capacity of the hash table SizeT Capacity() const; /// clear the hashtable void Clear(); /// return true if empty bool IsEmpty() const; /// add a key/value pair object to the hash table void Add(const KeyValuePair& kvp); /// add a key and associated value void Add(const KEYTYPE& key, const VALUETYPE& value); /// erase an entry void Erase(const KEYTYPE& key); /// return true if key exists in the array bool Contains(const KEYTYPE& key) const; /// return array of all key/value pairs in the table (slow) Array > Content() const; private: FixedArray > > hashArray; int size; }; //------------------------------------------------------------------------------ /** */ template HashTable::HashTable() : hashArray(128), size(0) { // empty } //------------------------------------------------------------------------------ /** */ template HashTable::HashTable(SizeT capacity) : hashArray(capacity), size(0) { // empty } //------------------------------------------------------------------------------ /** */ template HashTable::HashTable(const HashTable& rhs) : hashArray(rhs.hashArray), size(rhs.size) { // empty } //------------------------------------------------------------------------------ /** */ template void HashTable::operator=(const HashTable& rhs) { if (this != &rhs) { this->hashArray = rhs.hashArray; this->size = rhs.size; } } //------------------------------------------------------------------------------ /** */ template VALUETYPE& HashTable::operator[](const KEYTYPE& key) const { // get hash code from key, trim to capacity IndexT hashIndex = key.HashCode() % this->hashArray.Size(); const Array >& hashElements = this->hashArray[hashIndex]; int numHashElements = hashElements.Size(); #if NEBULA3_BOUNDSCHECKS n_assert(0 != numHashElements); // element with key doesn't exist #endif if (1 == numHashElements) { // no hash collissions, just return the only existing element return hashElements[0].Value(); } else { // here's a hash collision, find the right key // with a binary search IndexT hashElementIndex = hashElements.BinarySearchIndex(key); #if NEBULA3_BOUNDSCHECKS n_assert(InvalidIndex != hashElementIndex); #endif return hashElements[hashElementIndex].Value(); } } //------------------------------------------------------------------------------ /** */ template SizeT HashTable::Size() const { return this->size; } //------------------------------------------------------------------------------ /** */ template SizeT HashTable::Capacity() const { return this->hashArray.Size(); } //------------------------------------------------------------------------------ /** */ template void HashTable::Clear() { IndexT i; SizeT num = this->hashArray.Size(); for (i = 0; i < num; i++) { this->hashArray[i].Clear(); } this->size = 0; } //------------------------------------------------------------------------------ /** */ template bool HashTable::IsEmpty() const { return (0 == this->size); } //------------------------------------------------------------------------------ /** */ template void HashTable::Add(const KeyValuePair& kvp) { #if NEBULA3_BOUNDSCHECKS n_assert(!this->Contains(kvp.Key())); #endif IndexT hashIndex = kvp.Key().HashCode() % this->hashArray.Size(); this->hashArray[hashIndex].InsertSorted(kvp); this->size++; } //------------------------------------------------------------------------------ /** */ template void HashTable::Add(const KEYTYPE& key, const VALUETYPE& value) { KeyValuePair kvp(key, value); this->Add(kvp); } //------------------------------------------------------------------------------ /** */ template void HashTable::Erase(const KEYTYPE& key) { #if NEBULA3_BOUNDSCHECKS n_assert(this->size > 0); #endif IndexT hashIndex = key.HashCode() % this->hashArray.Size(); Array >& hashElements = this->hashArray[hashIndex]; IndexT hashElementIndex = hashElements.BinarySearchIndex(key); #if NEBULA3_BOUNDSCHECKS n_assert(InvalidIndex != hashElementIndex); // key doesn't exist #endif hashElements.EraseIndex(hashElementIndex); this->size--; } //------------------------------------------------------------------------------ /** */ template bool HashTable::Contains(const KEYTYPE& key) const { if (this->size > 0) { IndexT hashIndex = key.HashCode() % this->hashArray.Size(); Array >& hashElements = this->hashArray[hashIndex]; IndexT hashElementIndex = hashElements.BinarySearchIndex(key); return (InvalidIndex != hashElementIndex); } else { return false; } } //------------------------------------------------------------------------------ /** */ template Array > HashTable::Content() const { Array > result; int i; int num = this->hashArray.Size(); for (i = 0; i < num; i++) { if (this->hashArray[i].Size() > 0) { result.AppendArray(this->hashArray[i]); } } return result; } } // namespace Util //------------------------------------------------------------------------------