topical media & game development

talk show tell print

professional-program-23-Hashmap-FinalHashmap-hashmap.c

? / 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.