#ifndef _HASH_H #define _HASH_H //Hash.h // A HashTable f holds a set of(key, value) associations, just like an AssocList, // except all operations are O(1) (provided the hashing is good) // // basic operations are: // // f[key] = value -- assign a value associated with key // value = f[key] -- retrieve the value associated with key. // If there was no association, allocate a new association // with the value optionally set to some default. // // f.exists(key) -- does key have a value associated with it? // f.remove(key) -- remove the association for key (if one exists) // // f.clear() -- remove all associations // // f.keys() -- return array of existing keys // f.values() -- return array of existing values // // also: copy constructor, operator=, operator<< // // use like this: // // #include "Hash.h" // #include "Hash.cc" // // ... // // HashTable h(-1); // with default initialization value // HashTable g; // without default initialization value // // h["hi"] = 3; // // Array keys = h.keys(); // // for (int i = 0; i < keys.size(); ++i) // cout << h[keys[i]] << endl; // // Array keys = h.values(); // // h.remove("hi"); // // h.clear(); // // if (h.exists("hi")) ... // test if "hi" is a key without creating an entry for it // // g = h; // deep copy by operator= // #include #include "Array.h" #include "AssocList.h" #include "test_utilities.h" template class HashTable { public: //destructor ~HashTable(); // constructors HashTable(); HashTable(VALUE const & def); HashTable(HashTable const & a); // operator= by deep copy HashTable& operator=(HashTable const & a); // clear void clear(); // exists int exists(KEY const & k) const; // remove void remove(KEY const & k); // operator[] and operator[] const VALUE & operator[](KEY const & k); VALUE operator[](KEY const & k) const; // keys() and values() Array keys() const; Array values() const; // output friend std::ostream & operator<< <>(std::ostream &, const HashTable &); private: const int starting_modulus; unsigned int n_keys; unsigned int hash_modulus; Array > buckets; int hash(KEY const & k) const; int use_default; VALUE default_init; void grow(); }; // default hash function template int Hash(T const &s) { return (int) s; } // specialization for char *'s template <> int Hash(char * const & a) { char *s = a; int hash = 0; do hash += *s; while (*s++); return hash; } // operator<< template std::ostream & operator<<(std::ostream & out, const HashTable & a) { out << "n_keys = " << a.n_keys << ", hash_modulus = " << a.hash_modulus << "\nbuckets = {" << std::endl; for (int i = 0; i < a.buckets.size(); ++i) { if (i != 0) out << "," << std::endl; out << " bucket[" << i << "] = " << a.buckets[i]; } out << "\n}" << std::endl; return out; } // destructor template HashTable::~HashTable() {} // constructor without default value template HashTable::HashTable() : starting_modulus(7), n_keys(0), hash_modulus(starting_modulus), use_default(0) { } // constructor with default value template HashTable::HashTable(VALUE const &def) : starting_modulus(7), n_keys(0), hash_modulus(starting_modulus), use_default(1), default_init(def) { } // copy constructor template HashTable::HashTable(const HashTable& a) : starting_modulus(a.starting_modulus), n_keys(a.n_keys), hash_modulus(a.hash_modulus), buckets(a.buckets) // calls copy constructor for Array > { } // operator= template // deep copy HashTable& HashTable::operator=(const HashTable& a) { n_keys = a.n_keys; hash_modulus = a.hash_modulus; buckets = a.buckets; // deep copy, okay if &a == this return *this; } // clear template void HashTable::clear() { n_keys = 0; hash_modulus = starting_modulus; buckets.clear(); } // hash template int HashTable::hash(KEY const & k) const { return Hash(k) % hash_modulus; } // grow template void HashTable::grow() { const int new_modulus = hash_modulus * 3; const Array _keys = keys(); const HashTable copy(*this); // only works if copy constructor cannot call grow clear(); hash_modulus = new_modulus; for (int i = 0; i < _keys.size(); ++i) (*this)[_keys[i]] = copy[_keys[i]]; } // exists template int HashTable::exists(KEY const & k) const { return hash(k) < buckets.size() && buckets[hash(k)].exists(k); } // remove template void HashTable::remove(KEY const & k) { if (exists(k)) --n_keys; buckets[hash(k)].remove(k); } // operator[] and const operator[] template VALUE & HashTable::operator[](KEY const & k) { if (n_keys >= hash_modulus) grow(); if (! exists(k)) { ++n_keys; if (use_default) buckets[hash(k)][k] = default_init; } return buckets[hash(k)][k]; } template VALUE HashTable::operator[](KEY const & k) const { if (exists(k)) return buckets[hash(k)][k]; die("HashTable index out of range"); } // keys() and values() template Array HashTable::keys() const { Array keys; for (int i = 0; i < buckets.size(); ++i) { Array bucket_keys = buckets[i].keys(); for (int j = 0; j < bucket_keys.size(); ++j) keys[keys.size()] = bucket_keys[j]; } return keys; } template Array HashTable::values() const { Array values; for (int i = 0; i < buckets.size(); ++i) { Array bucket_values = buckets[i].values(); for (int j = 0; j < bucket_values.size(); ++j) values[values.size()] = bucket_values[j]; } return values; } #endif