#include "Col.ph" #ifdef __GNUG__ #pragma implementation #endif #include "HashTable.h" #include "Class.h" #include "Math.h" #include "Error.h" static const int cMinCapacity= 17; //---- HashTable --------------------------------------------------------------- NewAbstractMetaImpl(HashTable,Object, (TP(size), T(capacity), T(lowWatermark), T(highWatermark))); const int HashTable::cInvalidIndex= -1; HashTable::HashTable(int initCap, pEqualFunc eq, pHashFunc hs, int *aSize) { CalcBounds(initCap); equal= eq; hash= hs; if (aSize) size= aSize; else size= &mySize; *size= 0; } void HashTable::CalcBounds(int aCapacity) { capacity= Math::NextPrime(Math::Max(aCapacity, cMinCapacity)); lowWatermark= Math::Max(cMinCapacity, capacity / 4); highWatermark= (capacity * 3 / 4); // 0.75 } void HashTable::Empty(int) { AbstractMethod("Empty"); } void *HashTable::Add(void *op) { int slot; if (! SearchFirst(op, slot)) { SetKeyAt(op, slot); (*size)++; if ( *size >= highWatermark ) Rehash(NewCapacity(TRUE)); Changed(); return 0; } return op; } void *HashTable::Remove(void *op, EqualType eq) { int slot; op= eq ? SearchFirst(op, slot) : SearchPtr(op, slot); if (op) { RemoveSlot(slot); Changed(); } return op; } void *HashTable::Find(void *op, EqualType eq) { int slot; return eq ? SearchFirst(op, slot) : SearchPtr(op, slot); } int HashTable::IndexOf(void *op, EqualType eq) { int slot; op= eq ? SearchFirst(op, slot) : SearchPtr(op, slot); if (op) return slot; return -1; } //---- statistics ---- int HashTable::Collisions(void *obj) { int slot0, slot; slot0= HashAddress(obj); if (SearchFirst(obj, slot)) { if (slot >= slot0) return slot - slot0 + 1; return (slot + capacity) - slot0 + 1; } return 0; } double HashTable::AverageCollisions() { void *obj; double cum= 0.0; int i= GetInitialIndex(); while (obj= AtNextIndex(i)) { cum+= Collisions(obj); } if (*size > 0) return cum / *size; return 0; } //---- object i/o ---- OStream &HashTable::PrintOn(OStream &os) { MayNotUse("PrintOn"); return os; } IStream &HashTable::ReadFrom(IStream &is) { MayNotUse("ReadFrom"); return is; } //---- iterator support ---- void *HashTable::AtNextIndex(int &i) { void *op= 0; i++; while (i < capacity && ! (op= KeyAt(i))) i++; return op; } void *HashTable::AtPrevIndex(int &i) { void *op= 0; i--; while (i >= 0 && ! (op= KeyAt(i))) i--; return op; } int HashTable::GetInitialIndex() { return -1; } //---- algorithm ---- void *HashTable::SearchFirst(void *obj, int &slot) { void *op; slot= HashAddress(obj); while ((op= KeyAt(slot)) && ! equal(op, obj)) { if (++slot == capacity) slot= 0; } return op; } void *HashTable::SearchPtr(void *obj, int &slot) { if (SearchFirst(obj, slot) == obj) return obj; return 0; } void HashTable::RemoveSlot(int slot) { NullAt(slot); FixCollisions(slot); (*size)--; if (*size <= lowWatermark && *size > cMinCapacity) Rehash(NewCapacity(FALSE)); } void HashTable::FixCollisions(int slot) { void *cand; int candSlot0, emptySlot; bool swap; emptySlot= slot; if (++slot == capacity) slot= 0; while (cand= KeyAt(slot)) { #if 0 SearchFirst(cand, candSlot); // slow, but easy to understand if (candSlot == emptySlot) { Swap(emptySlot, slot); emptySlot= slot; } #else // fast, but difficult to understand candSlot0= HashAddress(cand); if (emptySlot < slot) swap= ! (emptySlot < candSlot0 && candSlot0 <= slot); else swap= (slot < candSlot0) && (candSlot0 < emptySlot); if (swap) { Swap(emptySlot, slot); emptySlot= slot; } #endif if (++slot == capacity) slot= 0; } } HashTable *HashTable::MakeNew(int) { // virtual constructor - returns a new object of the same class as this AbstractMethod("HashTable::MakeNew"); return 0; } void HashTable::Assign(HashTable *ht) { // hook - similar to a copy constructor lowWatermark= ht->lowWatermark; highWatermark= ht->highWatermark; capacity= ht->capacity; *size= *ht->size; } int HashTable::NewCapacity(bool grow) { if (grow) return capacity * 2; return capacity / 2; } void HashTable::Rehash(int newCapacity) { HashTable *ht= MakeNew(newCapacity); DoRehash(ht); Assign(ht); SafeDelete(ht); } void HashTable::DoRehash(HashTable *ht) { void *op; int i= GetInitialIndex(); while (op= AtNextIndex(i)) ht->Add(op); } void *HashTable::KeyAt(int) { AbstractMethod("KeyAt"); return 0; } void HashTable::SetKeyAt(void*, int) { AbstractMethod("SetKeyAt"); } void HashTable::NullAt(int) { AbstractMethod("NullAt"); } void HashTable::Swap(int, int) { AbstractMethod("Swap"); }