PROWAREtech
C++: Hashtable Example for String Keys
Generate a hash key from a string.
This first example is primarily in C but there is a little C++ sprinkled in here and there. The second example is pretty rich in C++ code.
For a more complete example, see the C++ template class example.
#include <iostream>
#include <string>
using namespace std;
#define TABLE_SIZE 1000003 // use a large prime number
unsigned int hash_func(const char *key, const unsigned int table_size)
{
unsigned int h = 0;
unsigned int o = 31415;
const unsigned int t = 27183;
while(*key)
{
h = (o * h + *key++) % table_size;
o = o * t % (table_size - 1);
}
return h;
}
class HashtableItem
{
private:
HashtableItem *pnext; // for a linked list
string key, value;
public:
HashtableItem(const string &key, const string &value) : pnext(nullptr)
{
this->key = key;
this->value = value;
}
~HashtableItem()
{
if(this->pnext)
delete this->pnext;
}
string Key() const
{
return this->key;
}
string Value() const
{
return this->value;
}
void SetNext(HashtableItem *item)
{
if(this->pnext)
this->pnext->SetNext(item);
else
this->pnext = item;
}
HashtableItem *GetNext() const
{
return this->pnext;
}
};
int main (int argc, char *argv[])
{
unsigned int i;
string key, value;
HashtableItem **table = (HashtableItem **)new void*[TABLE_SIZE];
for(i = 0; i < TABLE_SIZE; table[i++] = 0);
while(true)
{
cout << "enter a key (string) for the hash (q to quit): ";
cin >> key;
if(key == "q")
break;
i = hash_func(key.c_str(), TABLE_SIZE);
if(table[i])
{
if(table[i]->Key() == key) // then we found the item
cout << "the value at \"" << table[i]->Key() << "\" is \"" << table[i]->Value() << "\"\r\n";
else
{
HashtableItem *item;
for(item = table[i]; item->GetNext() && (item->GetNext()->Key() != key); item = item->GetNext());
if(item->GetNext()) // then we found the item in the linked list
cout << "the value at \"" << item->GetNext()->Key() << "\" is \"" << item->GetNext()->Value() << "\"\r\n";
else // then the key was not found in the linked list
{
cout << "enter a value for the key \"" << key << "\" (q to quit): ";
cin >> value;
if(value == "q")
break;
item->SetNext(new HashtableItem(key, value));
}
}
}
else // then the hashtable is functioning as it should WITHOUT the linked list
{
cout << "enter a value for the key \"" << key << "\" (q to quit): ";
cin >> value;
if(value == "q")
break;
table[i] = new HashtableItem(key, value);
}
}
// now print the entire table while deleting it
for(i = 0; i < TABLE_SIZE; i++)
{
if(table[i])
{
HashtableItem *item;
for(item = table[i]; item; item = item->GetNext())
cout << item->Key() << " = " << item->Value() << "\r\n";
delete table[i];
}
}
delete []table;
return 0;
}
And now an example in C++:
#include <iostream>
#include <string>
using namespace std;
#define TABLE_SIZE 1000003 // use a large prime number
unsigned int hash_func(const char *key, const unsigned int table_size)
{
unsigned int h = 0;
unsigned int o = 31415;
const unsigned int t = 27183;
while (*key)
{
h = (o * h + *key++) % table_size;
o = o * t % (table_size - 1);
}
return h;
}
class HashtableItem;
class Hashtable
{
private:
HashtableItem **table;
HashtableItem *cur_table_item;
int cur_index;
public:
Hashtable();
~Hashtable();
// Add a new entry, returns false when the key already exists
bool Add(const string &key, const string &value);
HashtableItem *operator[](const string &key) const;
// removes one table entry
void Remove(const string &key);
// removes all the table entries
void Clear();
// for looping through the table of kes/values
HashtableItem *GetFirst();
HashtableItem *GetNext();
};
class HashtableItem
{
private:
HashtableItem *pnext;
string key, value;
// keep these private to prevent the client from creating this object
HashtableItem(){}
HashtableItem(const string &key, const string &value);
~HashtableItem();
public:
const string &Key() const;
const string &Value() const;
const string &operator=(const string &value);
const char *operator=(const char *value);
// some friend functions that can access the private data
friend bool Hashtable::Add(const string &key, const string &value);
friend Hashtable::~Hashtable();
friend void Hashtable::Remove(const string &key);
friend HashtableItem *Hashtable::operator[](const string &key) const;
friend HashtableItem *Hashtable::GetNext();
friend void Hashtable::Clear();
};
// ##################### Hashtable ###########################
Hashtable::Hashtable() : cur_table_item(nullptr), cur_index(0)
{
table = new HashtableItem*[TABLE_SIZE];
for (int i = 0; i < TABLE_SIZE; table[i++] = nullptr);
}
Hashtable::~Hashtable()
{
for (int i = 0; i < TABLE_SIZE; i++)
{
if (table[i])
delete table[i];
}
delete []table;
}
bool Hashtable::Add(const string &key, const string &value)
{
unsigned int i = hash_func(key.c_str(), TABLE_SIZE);
if (table[i])
{
HashtableItem *node;
for (node = table[i]; node->pnext && (node->pnext->Key() != key); node = node->pnext);
if (node->pnext)
return false;
node->pnext = new HashtableItem(key, value);
return true;
}
table[i] = new HashtableItem(key, value);
return true;
}
HashtableItem *Hashtable::operator[](const string &key) const
{
unsigned int i = hash_func(key.c_str(), TABLE_SIZE);
if (table[i])
{
if (table[i]->Key() == key)
return table[i];
HashtableItem *node;
for (node = table[i]; node->pnext && (node->pnext->Key() != key); node = node->pnext);
if (node->pnext)
return node->pnext;
}
return nullptr;
}
void Hashtable::Remove(const string &key)
{
unsigned int i = hash_func(key.c_str(), TABLE_SIZE);
if (table[i])
{
HashtableItem *node, *tmp;
if (table[i]->Key() == key)
{
tmp = table[i]->pnext;
table[i]->pnext = nullptr;
delete table[i];
table[i] = tmp;
}
else
{
for (node = table[i]; node->pnext && (node->pnext->Key() != key); node = node->pnext);
if (node->pnext) // then key found in linked list
{
tmp = node->pnext->pnext;
node->pnext = nullptr;
delete node->pnext;
node->pnext = tmp;
}
}
}
}
void Hashtable::Clear()
{
for (int i = 0; i < TABLE_SIZE; i++)
{
delete table[i];
table[i] = nullptr;
}
}
HashtableItem *Hashtable::GetFirst()
{
int i;
this->cur_table_item = nullptr;
this->cur_index = 0;
for (i = this->cur_index; i < TABLE_SIZE && table[i] == nullptr; i++);
if (i < TABLE_SIZE)
{
this->cur_table_item = table[i];
this->cur_index = i;
}
return this->cur_table_item;
}
HashtableItem *Hashtable::GetNext()
{
if (this->cur_table_item && this->cur_table_item->pnext)
this->cur_table_item = this->cur_table_item->pnext;
else
{
int i;
for (i = this->cur_index + 1; i < TABLE_SIZE && table[i] == nullptr; i++);
if (i < TABLE_SIZE)
{
this->cur_table_item = table[i];
this->cur_index = i;
}
else
{
this->cur_table_item = nullptr;
this->cur_index = 0;
}
}
return this->cur_table_item;
}
// ##################### HashtableItem ###########################
HashtableItem::HashtableItem(const string &xKey, const string &xValue) : key(xKey), value(xValue), pnext(nullptr) {}
HashtableItem::~HashtableItem()
{
if (this->pnext)
delete this->pnext;
}
const string &HashtableItem::Key() const
{
return this->key;
}
const string &HashtableItem::Value() const
{
return this->value;
}
const string &HashtableItem::operator=(const string &value)
{
this->value = value;
return value;
}
const char *HashtableItem::operator=(const char *value)
{
this->value = value;
return value;
}
// #################### entry point ####################
// notice how much cleaner the code is here than when compared to the C example
int main(int argc, char *argv[])
{
string key, value;
Hashtable ht;
HashtableItem *item;
while (true)
{
cout << "enter a key (string) for the hash (q to quit): ";
cin >> key;
if (key == "q")
break;
item = ht[key];
if (item == nullptr)
{
cout << "enter a value for the key \"" << key << "\" (q to quit): ";
cin >> value;
if (value == "q")
break;
ht.Add(key, value);
}
else
{
cout << "the value at \"" << item->Key() << "\" is \"" << item->Value() << "\"\r\nenter a new value: ";
cin >> value;
(*item) = value;
}
}
while (true)
{
item = ht.GetFirst();
while (item)
{
cout << "the value at \"" << item->Key() << "\" is \"" << item->Value() << "\"\r\n";
item = ht.GetNext();
}
cout << "enter the key (string) to delete (q to quit): ";
cin >> key;
if (key == "q")
break;
ht.Remove(key);
}
return 0;
}
To convert this class to use C++ templates click here. To see the integer implementation of this class click here.
Comment