? / professional-program-23-Hashmap-FinalHashmap-hashmap.c
include <iterator> // Throws invalid_argument if numBuckets is non-positive template <typename T> DefaultHash<T>::DefaultHash(int numBuckets) throw (invalid_argument) { if (numBuckets <= 0) { throw (invalid_argument("numBuckets must be > 0")); } mNumBuckets = numBuckets; } // Uses the division method for hashing. // Treats the key as a sequence of bytes, sums the ASCII // values of the bytes, and mods the total by the number // of buckets. template <typename T> int DefaultHash<T>::hash(const T& key) const { int bytes = sizeof(key); unsigned long res = 0; for (int i = 0; i < bytes; ++i) { res += *((char*)&key + i); } return (res % mNumBuckets); } // Throws invalid_argument if numBuckets is non-positive DefaultHash<string>::DefaultHash(int numBuckets) throw (invalid_argument) { if (numBuckets <= 0) { throw (invalid_argument("numBuckets must be > 0")); } mNumBuckets = numBuckets; } // Uses the division method for hashing after summing the // ASCII values of all the characters in key. int DefaultHash<string>::hash(const string& key) const { int sum = 0; for (size_t i = 0; i < key.size(); i++) { sum += key[i]; } return (sum % mNumBuckets); } // Dereferencing or incrementing an iterator constructed with the // default ctor is undefined, so it doesn't matter what values we give // here. template<typename Key, typename T, typename Compare, typename Hash> HashIterator<Key, T, Compare, Hash>::HashIterator() { mBucket = -1; mIt = list<pair<const Key, T> >::iterator(); mHashmap = NULL; } template<typename Key, typename T, typename Compare, typename Hash> HashIterator<Key, T, Compare, Hash>::HashIterator(int bucket, typename list<pair<const Key, T> >::iterator listIt, const hashmap<Key, T, Compare, Hash>* inHashmap) : mBucket(bucket), mIt(listIt), mHashmap(inHashmap) { } // Return the actual element template<typename Key, typename T, typename Compare, typename Hash> pair<const Key, T>& HashIterator<Key, T, Compare, Hash>::operator*() const { return (*mIt); } // Return the iterator, so the compiler can apply -> to it to access // the actual desired field. template<typename Key, typename T, typename Compare, typename Hash> pair<const Key, T>* HashIterator<Key, T, Compare, Hash>::operator->() const { return (&(*mIt)); } // Defer the details to the increment() helper template<typename Key, typename T, typename Compare, typename Hash> HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator++() { increment(); return (*this); } // Defer the details to the increment() helper template<typename Key, typename T, typename Compare, typename Hash> const HashIterator<Key, T, Compare, Hash> HashIterator<Key, T, Compare, Hash>::operator++(int) { HashIterator<Key, T, Compare, Hash> oldIt = *this; increment(); return (oldIt); } // Defer the details to the decrement() helper template<typename Key, typename T, typename Compare, typename Hash> HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator--() { decrement(); return (*this); } // Defer the details to the decrement() helper template<typename Key, typename T, typename Compare, typename Hash> const HashIterator<Key, T, Compare, Hash> HashIterator<Key, T, Compare, Hash>::operator--(int) { HashIterator<Key, T, Compare, Hash> newIt = *this; decrement(); return (newIt); } // Behavior is undefined if mIt already refers to the past-the-end // element in the table, or is otherwise invalid. template<typename Key, typename T, typename Compare, typename Hash> void HashIterator<Key, T, Compare, Hash>::increment() { // mIt is an iterator into a single bucket. // Increment it. ++mIt; // If we're at the end of the current bucket, // find the next bucket with elements. if (mIt == (*mHashmap->mElems)[mBucket].end()) { for (size_t i = mBucket + 1; i < (*mHashmap->mElems).size(); i++) { if (!((*mHashmap->mElems)[i].empty())) { // We found a non-empty bucket. // Make mIt refer to the first element in it. mIt = (*mHashmap->mElems)[i].begin(); mBucket = i; return; } } // No more empty buckets. Assign mIt to refer to the end // iterator of the last list. mBucket = (*mHashmap->mElems).size() - 1; mIt = (*mHashmap->mElems)[mBucket].end(); } } // Behavior is undefined if mIt already refers to the first element // in the table, or is otherwise invalid. template<typename Key, typename T, typename Compare, typename Hash> void HashIterator<Key, T, Compare, Hash>::decrement() { // mIt is an iterator into a single bucket. // If it's at the beginning of the current bucket, don't decrement it. // Instead, try to find a non-empty bucket ahead of the current one. if (mIt == (*mHashmap->mElems)[mBucket].begin()) { for (int i = mBucket - 1; i >= 0; --i) { if (!((*mHashmap->mElems)[i].empty())) { mIt = (*mHashmap->mElems)[i].end(); --mIt; mBucket = i; return; } } // No more non-empty buckets. This is an invalid decrement. // Assign mIt to refer to one before the start element of the first // list (an invalid position). mIt = (*mHashmap->mElems)[0].begin(); --mIt; mBucket = 0; } else { // We're not at the beginning of the bucket, so // just move down. --mIt; } } template<typename Key, typename T, typename Compare, typename Hash> bool HashIterator<Key, T, Compare, Hash>::operator==( const HashIterator& rhs) const { // All fields, including the hashmap to which the iterators refer, // must be equal. return (mHashmap == rhs.mHashmap && mBucket == rhs.mBucket && mIt == rhs.mIt); } template<typename Key, typename T, typename Compare, typename Hash> bool HashIterator<Key, T, Compare, Hash>::operator!=( const HashIterator& rhs) const { return (!operator==(rhs)); } // Construct mElems with the number of buckets. template <typename Key, typename T, typename Compare, typename Hash> hashmap<Key, T, Compare, Hash>::hashmap( const Compare& comp, const Hash& hash) throw(invalid_argument) : mSize(0), mComp(comp), mHash(hash) { if (mHash.numBuckets() <= 0) { throw (invalid_argument("Number of buckets must be positive")); } mElems = new vector<list<value_type> >(mHash.numBuckets()); } // Make a call to insert() to actually insert the elements. template <typename Key, typename T, typename Compare, typename Hash> template <class InputIterator> hashmap<Key, T, Compare, Hash>::hashmap( InputIterator first, InputIterator last, const Compare& comp, const Hash& hash) throw(invalid_argument) : mSize(0), mComp(comp), mHash(hash) { if (mHash.numBuckets() <= 0) { throw (invalid_argument("Number of buckets must be positive")); } mElems = new vector<list<value_type> >(mHash.numBuckets()); insert(first, last); } template <typename Key, typename T, typename Compare, typename Hash> hashmap<Key, T, Compare, Hash>::~hashmap() { delete mElems; } template <typename Key, typename T, typename Compare, typename Hash> hashmap<Key, T, Compare, Hash>::hashmap(const hashmap< Key, T, Compare, Hash>& src) : mSize(src.mSize), mComp(src.mComp), mHash(src.mHash) { // Don't need to bother checking if numBuckets is positive, because // we know src checked. // Use the vector copy constructor mElems = new vector<list<value_type> >(*(src.mElems)); } template <typename Key, typename T, typename Compare, typename Hash> hashmap<Key, T, Compare, Hash>& hashmap<Key, T, Compare, Hash>::operator=( const hashmap<Key, T, Compare, Hash>& rhs) { // check for self-assignment if (this != &rhs) { delete mElems; mSize = rhs.mSize; mComp = rhs.mComp; mHash = rhs.mHash; // Don't need to bother checking if numBuckets is positive, because // we know rhs checked // Use the vector copy constructor mElems = new vector<list<value_type> >(*(rhs.mElems)); } return (*this); } template <typename Key, typename T, typename Compare, typename Hash> typename list<pair<const Key, T> >::iterator hashmap<Key, T, Compare, Hash>::findElement(const key_type& x, int& bucket) const { // hash the key to get the bucket bucket = mHash.hash(x); // Look for the key in the bucket for (typename ListType::iterator it = (*mElems)[bucket].begin(); it != (*mElems)[bucket].end(); ++it) { if (mComp(it->first, x)) { return (it); } } return ((*mElems)[bucket].end()); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::find(const key_type& x) { int bucket; // Use the findElement() helper typename ListType::iterator it = findElement(x, bucket); if (it == (*mElems)[bucket].end()) { // we didn't find the element -- return the end iterator return (end()); } // We found the element -- convert the bucket/iterator to a HashIterator return (HashIterator<Key, T, Compare, Hash>(bucket, it, this)); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::const_iterator hashmap<Key, T, Compare, Hash>::find(const key_type& x) const { int bucket; // Use the findElement() helper typename ListType::iterator it = findElement(x, bucket); if (it == (*mElems)[bucket].end()) { // we didn't find the element -- return the end iterator return (end()); } // We found the element -- convert the bucket/iterator to a HashIterator return (HashIterator<Key, T, Compare, Hash>(bucket, it, this)); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::size_type hashmap<Key, T, Compare, Hash>::count(const key_type& x) const { // There are either 1 or 0 elements matching key x. // If we can find a match, return 1, otherwise return 0. if (find(x) == end()) { return (0); } else { return (1); } } template <typename Key, typename T, typename Compare, typename Hash> T& hashmap<Key, T, Compare, Hash>::operator[] (const key_type& x) { // This definition is the same as that used by map, according to // the standard. // It's a bit cryptic, but it basically attempts to insert // a new key/value pair of x and a new value. Regardless of whether // the insert succeeds or fails, insert() returns a pair of an // iterator/bool. The iterator refers to a key/value pair, the // second element of which is the value we want to return. return (((insert(make_pair(x, T()))).first)->second); } template <typename Key, typename T, typename Compare, typename Hash> pair<typename hashmap<Key, T, Compare, Hash>::iterator, bool> hashmap<Key, T, Compare, Hash>::insert(const value_type& x) { int bucket; // Try to find the element typename ListType::iterator it = findElement(x.first, bucket); if (it != (*mElems)[bucket].end()) { // The element already exists // Convert the list iterator into a HashIterator, which // also requires the bucket and a pointer to the hashmap. HashIterator<Key, T, Compare, Hash> newIt(bucket, it, this); // Some compilers don't like make_pair here pair<HashIterator<Key, T, Compare, Hash>, bool> p(newIt, false); return (p); } else { // We didn't find the element, so insert a new one. mSize++; typename ListType::iterator endIt = (*mElems)[bucket].insert((*mElems)[bucket].end(), x); pair<HashIterator<Key, T, Compare, Hash>, bool> p( HashIterator<Key, T, Compare, Hash>(bucket, endIt, this), true); return (p); } } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::insert(typename hashmap<Key, T, Compare, Hash>::iterator position, const value_type& x) { // completely ignore position return (insert(x).first); } template <typename Key, typename T, typename Compare, typename Hash> template <class InputIterator> void hashmap<Key, T, Compare, Hash>::insert(InputIterator first, InputIterator last) { // Copy each element in the range by using an insert_iterator // adapter. Give begin() as a dummy position -- insert ignores it // anyway. std::insert_iterator<hashmap<Key, T, Compare, Hash> > it(*this, begin()); copy(first, last, it); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::size_type hashmap<Key, T, Compare, Hash>::erase(const key_type& x) { int bucket; // First, try to find the element typename ListType::iterator it = findElement(x, bucket); if (it != (*mElems)[bucket].end()) { // The element already exists -- erase it (*mElems)[bucket].erase(it); mSize--; return (1); } else { return (0); } } template <typename Key, typename T, typename Compare, typename Hash> void hashmap<Key, T, Compare, Hash>::erase( hashmap<Key, T, Compare, Hash>::iterator position) { // Erase the element from its bucket (*mElems)[position.mBucket].erase(position.mIt); mSize--; } template <typename Key, typename T, typename Compare, typename Hash> void hashmap<Key, T, Compare, Hash>::erase( hashmap<Key, T, Compare, Hash>::iterator first, hashmap<Key, T, Compare, Hash>::iterator last) { typename hashmap<Key, T, Compare, Hash>::iterator cur, next; // erase all the elements in the range for (next = first; next != last; ) { cur = next++; erase(cur); } } template <typename Key, typename T, typename Compare, typename Hash> void hashmap<Key, T, Compare, Hash>::clear() { // Call clear on each list. for_each(mElems->begin(), mElems->end(), mem_fun_ref(&ListType::clear)); mSize = 0; } template <typename Key, typename T, typename Compare, typename Hash> bool hashmap<Key, T, Compare, Hash>::empty() const { return (mSize == 0); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::size_type hashmap<Key, T, Compare, Hash>::size() const { return (mSize); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::size_type hashmap<Key, T, Compare, Hash>::max_size() const { // In the worst case, all the elements hash to the // same list, so the max_size is the max_size of a single // list. This code assumes that all the lists have the same // max_size. return ((*mElems)[0].max_size()); } // Just swap the four data members // Use the generic swap template template <typename Key, typename T, typename Compare, typename Hash> void hashmap<Key, T, Compare, Hash>::swap(hashmap< Key, T, Compare, Hash>& hashIn) { // explicitly qualify with std:: so the compiler doesn't think // it's a recursive call. std::swap(*this, hashIn); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::begin() { if (mSize == 0) { // Special case: there are no elements, so return the end iterator return (end()); } // We know there is at least one element. Find the first element for (size_t i = 0; i < mElems->size(); ++i) { if (!((*mElems)[i].empty())) { return (HashIterator<Key, T, Compare, Hash>(i, (*mElems)[i].begin(), this)); } } // should never reach here, but if we do, return the end iterator return (end()); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::end() { // The end iterator is just the end iterator of the list in last bucket return (HashIterator<Key, T, Compare, Hash>(mElems->size() - 1, (*mElems)[mElems->size() - 1].end(), this)); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::begin() const { if (mSize == 0) { // Special case: there are no elements, so return the end iterator return (end()); } // We know there is at least one element. Find the first element for (size_t i = 0; i < mElems->size(); ++i) { if (!((*mElems)[i].empty())) { return (HashIterator<Key, T, Compare, Hash>(i, (*mElems)[i].begin(), this)); } } // should never reach here, but if we do, return the end iterator return (end()); } template <typename Key, typename T, typename Compare, typename Hash> typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::end() const { // The end iterator is just the end iterator of the list in last bucket return (HashIterator<Key, T, Compare, Hash>(mElems->size() - 1, (*mElems)[mElems->size() - 1].end(), this)); }
(C) Æliens 20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.