参考 C++ 高级编程,实现一个 STL 容器:基本 hashmap
hashmap.h
#include <stdexcept>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>
using namespace std;
template<typename T>
class DefaultHash
{
public:
DefaultHash(int numBuckets = 101) throw(std::invalid_argument);
int hash(const T& key) const;
int numBuckets() const
{
return mNumBuckets;
}
private:
int mNumBuckets;
};
template<typename T>
DefaultHash<T>::DefaultHash(int numBuckets) throw(std::invalid_argument)
{
if (numBuckets <= 0)
{
throw std::invalid_argument("numBuckets must be > 0");
}
mNumBuckets = numBuckets;
}
template<typename T>
int DefaultHash<T>::hash(const T& key) const
{
int bytes = sizeof(key);
unsigned long ret = 0;
for (int i = 0; i < bytes; ++i)
{
ret += *((char*)&key + i);
}
return ret % mNumBuckets;
}
template<>
class DefaultHash<string>
{
public:
DefaultHash(int numBuckets = 101) throw(std::invalid_argument);
int hash(const string& key) const;
int numBuckets() const
{
return mNumBuckets;
}
private:
int mNumBuckets;
};
DefaultHash<string>::DefaultHash(int numBuckets) throw(std::invalid_argument)
{
if (numBuckets <= 0)
{
throw std::invalid_argument("numBuckets must be > 0");
}
mNumBuckets = numBuckets;
}
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;
}
template<typename Key, typename T, typename Compare, typename Hash>
class HashIterator;
template<typename Key, typename T, typename Compare = std::equal_to<Key>, typename Hash = DefaultHash<Key> >
class hashmap
{
public:
typedef Key key_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type;
typedef pair<const Key, T>& reference;
typedef const pair<const Key, T>& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef HashIterator<Key, T, Compare, Hash> iterator;
typedef HashIterator<Key, T, Compare, Hash> const_iterator;
typedef Compare key_compare;
class value_compare
: public std::binary_function<value_type, value_type, bool>
{
public:
bool operator()(const value_type& x, const value_type& y) const
{
return comp(x.first, y.first);
}
friend class hashmap<Key, T, Compare, Hash>;
private:
value_compare(Compare c)
: comp(c)
{
}
Compare comp;
};
friend class HashIterator<Key, T, Compare, Hash>;
explicit hashmap(const Compare& comp = Compare(), const Hash& hash = Hash()) throw(std::invalid_argument);
template<typename InputIterator>
hashmap(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Hash& = Hash()) throw(std::invalid_argument);
~hashmap();
hashmap(const hashmap<Key, T, Compare, Hash>& src);
hashmap<Key, T, Compare, Hash>& operator=(const hashmap<Key, T, Compare, Hash>& rhs);
template<typename InputIterator>
void insert(InputIterator first, InputIterator last);
pair<iterator, bool> insert(const value_type& x);
iterator insert(iterator position, const value_type& x);
void erase(iterator position);
size_type erase(const key_type& x);
void erase(iterator first, iterator last);
void clear();
key_compare key_comp() const;
value_compare value_comp() const;
iterator find(const key_type& x);
const_iterator find(const key_type& x) const;
T& operator[](const key_type& x);
bool empty() const;
size_type size() const;
size_type max_size() const;
size_type count(const key_type& x) const;
void swap(hashmap<Key, T, Compare, Hash>& hashIn);
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
typedef list<value_type> ListType;
typename ListType::iterator findElement(const key_type& x, int& bucket) const;
vector<ListType>* mElems;
int mSize;
Compare mComp;
Hash mHash;
};
template<typename Key, typename T, typename Compare, typename Hash>
hashmap<Key, T, Compare, Hash>::hashmap(const Compare& comp, const Hash& hash) throw(std::invalid_argument)
: mSize(0), mComp(comp), mHash(hash)
{
if (mHash.numBuckets() <= 0)
{
throw std::invalid_argument("number of buckets must be positive");
}
mElems = new vector<ListType>(mHash.numBuckets());
}
template<typename Key, typename T, typename Compare, typename Hash>
template<typename InputIterator>
hashmap<Key, T, Compare, Hash>::hashmap(InputIterator first, InputIterator last, const Compare& comp, const Hash& hash) throw(std::invalid_argument)
: mSize(0), mComp(comp), mHash(hash)
{
if (mHash.numBuckets() <= 0)
{
throw std::invalid_argument("number of buckets must be positive");
}
mElems = new vector<ListType>(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)
{
mElems = new vector<ListType>(*(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)
{
if (this != &rhs)
{
delete mElems;
mSize = rhs.mSize;
mComp = rhs.mComp;
mHash = rhs.mHash;
mElems = new vector<ListType>(*(rhs.mElems));
}
return *this;
}
template<typename Key, typename T, typename Compare, typename Hash>
typename hashmap<Key, T, Compare, Hash>::ListType::iterator hashmap<Key, T, Compare, Hash>::findElement(const key_type& x, int& bucket) const
{
bucket = mHash.hash(x);
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;
typename ListType::iterator it = findElement(x, bucket);
if (it == (*mElems)[bucket].end())
{
return end();
}
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;
typename ListType::iterator it = findElement(x, bucket);
if (it == (*mElems)[bucket].end())
{
return end();
}
return HashIterator<Key, T, Compare, Hash>(bucket, it, this);
}
template<typename Key, typename T, typename Compare, typename Hash>
T& hashmap<Key, T, Compare, Hash>::operator[](const key_type& x)
{
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;
typename ListType::iterator it = findElement(x.first, bucket);
if (it != (*mElems)[bucket].end())
{
HashIterator<Key, T, Compare, Hash> newIt(bucket, it, this);
pair<HashIterator<Key, T, Compare, Hash>, bool> p(newIt, false);
return p;
}
else
{
++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>
template<typename InputIterator>
void hashmap<Key, T, Compare, Hash>::insert(InputIterator first, InputIterator last)
{
insert_iterator<hashmap<Key, T, Compare, Hash> > inserter(*this, begin());
copy(first, last, inserter);
}
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)
{
(void)position;
return insert(x).first;
}
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;
typename ListType::iterator it = findElement(x, bucket);
if (it != (*mElems)[bucket].end())
{
(*mElems)[bucket].erase(it);
--mSize;
return 1;
}
return 0;
}
template<typename Key, typename T, typename Compare, typename Hash>
void hashmap<Key, T, Compare, Hash>::erase(typename hashmap<Key, T, Compare, Hash>::iterator position)
{
(*mElems)[position.getBucket()].erase(position.getIterator());
--mSize;
}
template<typename Key, typename T, typename Compare, typename Hash>
void hashmap<Key, T, Compare, Hash>::erase(typename hashmap<Key, T, Compare, Hash>::iterator first, typename hashmap<Key, T, Compare, Hash>::iterator last)
{
typename hashmap<Key, T, Compare, Hash>::iterator cur, next;
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()
{
for_each(mElems->begin(), mElems->end(), mem_fun_ref(&ListType::clear));
mSize = 0;
}
template<typename Key, typename T, typename Compare, typename Hash>
typename hashmap<Key, T, Compare, Hash>::key_compare hashmap<Key, T, Compare, Hash>::key_comp() const
{
return mComp;
}
template<typename Key, typename T, typename Compare, typename Hash>
typename hashmap<Key, T, Compare, Hash>::value_compare hashmap<Key, T, Compare, Hash>::value_comp() const
{
return value_compare(mComp);
}
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
{
return (*mElems)[0].max_size();
}
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
{
if (find(x) == end())
{
return 0;
}
else
{
return 1;
}
}
template<typename Key, typename T, typename Compare, typename Hash>
void hashmap<Key, T, Compare, Hash>::swap(hashmap<Key, T, Compare, Hash>& hashIn)
{
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)
{
return end();
}
for (size_t i = 0; i < mElems->size(); ++i)
{
if (!((*mElems)[i].empty()))
{
return HashIterator<Key, T, Compare, Hash>(i, (*mElems)[i].begin(), this);
}
}
return end();
}
template<typename Key, typename T, typename Compare, typename Hash>
typename hashmap<Key, T, Compare, Hash>::iterator hashmap<Key, T, Compare, Hash>::end()
{
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>::const_iterator hashmap<Key, T, Compare, Hash>::begin() const
{
if (mSize == 0)
{
return end();
}
for (size_t i = 0; i < mElems->size(); ++i)
{
if (!((*mElems)[i].empty()))
{
return HashIterator<Key, T, Compare, Hash>(i, (*mElems)[i].begin(), this);
}
}
return end();
}
template<typename Key, typename T, typename Compare, typename Hash>
typename hashmap<Key, T, Compare, Hash>::const_iterator hashmap<Key, T, Compare, Hash>::end() const
{
return HashIterator<Key, T, Compare, Hash>(mElems->size() - 1, (*mElems)[mElems->size() - 1].end(), this);
}
template<typename Key, typename T, typename Compare, typename Hash>
class HashIterator : public std::iterator<std::bidirectional_iterator_tag, pair<const Key, T> >
{
public:
HashIterator();
HashIterator(int bucket, typename list<pair<const Key, T> >::iterator listIt, const hashmap<Key, T, Compare, Hash>* inHashMap);
pair<const Key, T>& operator*() const;
pair<const Key, T>* operator->() const;
HashIterator<Key, T, Compare, Hash>& operator++();
const HashIterator<Key, T, Compare, Hash> operator++(int);
HashIterator<Key, T, Compare, Hash>& operator--();
const HashIterator<Key, T, Compare, Hash> operator--(int);
bool operator==(const HashIterator& rhs) const;
bool operator!=(const HashIterator& rhs) const;
const HashIterator<Key, T, Compare, Hash>& operator+(int);
const HashIterator<Key, T, Compare, Hash>& operator-(int);
int getBucket() const
{
return mBucket;
}
typename list<pair<const Key, T> >::iterator getIterator() const
{
return mIt;
}
private:
void increment();
void decrement();
int mBucket;
typename list<pair<const Key, T> >::iterator mIt;
const hashmap<Key, T, Compare, Hash>* mHashMap;
};
template<typename Key, typename T, typename Compare, typename Hash>
HashIterator<Key, T, Compare, Hash>::HashIterator()
{
mBucket = -1;
mIt = typename 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)
{
}
template<typename Key, typename T, typename Compare, typename Hash>
pair<const Key, T>& HashIterator<Key, T, Compare, Hash>::operator*() const
{
return *mIt;
}
template<typename Key, typename T, typename Compare, typename Hash>
pair<const Key, T>* HashIterator<Key, T, Compare, Hash>::operator->() const
{
return &(*mIt);
}
template<typename Key, typename T, typename Compare, typename Hash>
HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator++()
{
increment();
return *this;
}
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;
}
template<typename Key, typename T, typename Compare, typename Hash>
HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator--()
{
decrement();
return *this;
}
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;
}
template<typename Key, typename T, typename Compare, typename Hash>
bool HashIterator<Key, T, Compare, Hash>::operator==(const HashIterator& rhs) const
{
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));
}
template<typename Key, typename T, typename Compare, typename Hash>
const HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator+(int step)
{
for (int i = 0; i < step; ++i)
{
increment();
}
return *this;
}
template<typename Key, typename T, typename Compare, typename Hash>
const HashIterator<Key, T, Compare, Hash>& HashIterator<Key, T, Compare, Hash>::operator-(int step)
{
for (int i = 0; i < step; ++i)
{
decrement();
}
return *this;
}
template<typename Key, typename T, typename Compare, typename Hash>
void HashIterator<Key, T, Compare, Hash>::increment()
{
++mIt;
if (mIt == (*(mHashMap->mElems))[mBucket].end())
{
for (size_t i = mBucket + 1; i < (*(mHashMap->mElems)).size(); ++i)
{
if (!((*(mHashMap->mElems))[i].empty()))
{
mIt = (*(mHashMap->mElems))[i].begin();
mBucket = i;
return;
}
}
mBucket = (*(mHashMap->mElems)).size() - 1;
mIt = (*(mHashMap->mElems))[mBucket].end();
}
}
template<typename Key, typename T, typename Compare, typename Hash>
void HashIterator<Key, T, Compare, Hash>::decrement()
{
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;
}
}
mIt = (*(mHashMap->mElems))[0].begin();
--mIt;
mBucket = 0;
}
else
{
--mIt;
}
}
test.cpp
#include "hashmap.h"
#include <iostream>
#include <map>
using namespace std;
void printHashMap(const hashmap<string, int>& myMap);
int main()
{
DefaultHash<int> hash;
cout << hash.hash(23) << endl;;
DefaultHash<string> hash2;
cout << hash2.hash("my hashmap") << endl;
hashmap<int, int> hm;
hm.insert(make_pair(4, 40));
hm.insert(make_pair(5, 50));
hashmap<int, int>::iterator x = hm.find(4);
if (x != hm.end())
{
cout << "4 maps to " << x->second << endl;
}
else
{
cout << "can not find 4 in map" << endl;
}
cout << hm.size() << " " << hm.max_size() << endl;
hm.erase(4);
x = hm.find(4);
if (x != hm.end())
{
cout << "4 maps to " << x->second << endl;
}
else
{
cout << "can not find 4 in map" << endl;
}
cout << hm.size() << " " << hm.max_size() << endl;
hm[4] = 35;
cout << hm[4] << endl;
cout << hm.size() << " " << hm.max_size() << endl;
list<pair<int, int> > l;
vector<list<pair<int, int> > > v;
cout << l.max_size() << " " << v.max_size() << endl;
hashmap<string, int> myHash;
cout << boolalpha << myHash.empty() << endl;
myHash.insert(make_pair("KeyOne", 100));
myHash.insert(make_pair("KeyTwo", 200));
myHash.insert(make_pair("KeyThree", 300));
myHash.insert(myHash.begin(), make_pair("KeyFour", 400));
myHash.insert(myHash.end(), make_pair("KeyFive", 500));
cout << boolalpha << myHash.empty() << endl;
cout << myHash.count("KeyFive") << " " << myHash.count("KeySix") << endl;
printHashMap(myHash);
map<string, int> myMap;
myMap.insert(make_pair("KeyMap", 1000));
myMap.insert(make_pair("KeyMap2", 2000));
myMap.insert(make_pair("KeyMap3", 3000));
hashmap<string, int> myHash2(myMap.begin(), myMap.end());
printHashMap(myHash2);
myHash2.swap(myHash);
printHashMap(myHash);
printHashMap(myHash2);
myHash2.swap(myHash);
myHash.insert(myMap.begin(), myMap.end());
printHashMap(myHash);
hashmap<string, int> myHash3(myHash);
printHashMap(myHash3);
hashmap<string, int> myHash4 = myHash;
printHashMap(myHash4);
map<string, int> myMap2(myHash.begin(), myHash.end());
for (map<string, int>::iterator it = myMap2.begin(); it != myMap2.end(); ++it)
{
cout << it->first << " STL maps to " << (*it).second << endl;
}
cout << endl;
myHash.erase("KeyFour");
printHashMap(myHash);
myHash.erase("KeySix");
printHashMap(myHash);
printHashMap(myHash2);
myHash2.erase(myHash2.begin() + 1);
printHashMap(myHash2);
myHash2.erase(myHash2.end() - 1);
printHashMap(myHash2);
myHash2.erase(myHash2.end() - 1);
printHashMap(myHash2);
cout << boolalpha << myHash2.empty() << endl;
myHash3.erase(myHash3.begin(), myHash3.end());
cout << boolalpha << myHash3.empty() << endl;
myHash.clear();
cout << boolalpha << myHash.empty() << endl;
return 0;
}
void printHashMap(const hashmap<string, int>& myMap)
{
for (hashmap<string, int>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
cout << it->first << " maps to " << (*it).second << endl;
}
cout << endl;
}