PROWAREtech
C++: Hashtable Example for Integer Keys
Use modulus to find the integer key.
This first example is primarily in C but there is a little C++ sprinkled in here and there to make the code more readable. The second example is pure C++.
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
#define HASH(key, table_size) (key % table_size)
class HashtableItem
{
private:
HashtableItem *pnext; // for a linked list
unsigned int key;
string value;
public:
HashtableItem(const unsigned int key, const string &value) : pnext(nullptr)
{
this->key = key;
this->value = value;
}
~HashtableItem()
{
if(this->pnext)
delete this->pnext;
}
unsigned int 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, key;
string value;
HashtableItem **table = (HashtableItem **)new void*[TABLE_SIZE];
for(i = 0; i < TABLE_SIZE; table[i++] = 0);
while(true)
{
cout << "enter a key (integer) for the hash (0 to quit): ";
cin >> key;
if(0 == key)
break;
i = HASH(key, 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 (string) 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 (string) 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 1000004 // use a large number multiple of two
#define HASH_MACRO(key, table_size) (key & (table_size - 1))
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 unsigned int key, const string &value);
HashtableItem *operator[](const unsigned int key) const;
// removes one item
void Remove(const unsigned int key);
// removes all items
void Clear();
// for looping through the keys
HashtableItem *GetFirst();
HashtableItem *GetNext();
};
class HashtableItem
{
private:
HashtableItem *pnext;
unsigned int key;
string value;
// keep these private because it prevents the client from creating them directly
HashtableItem() {}
HashtableItem(const unsigned int key, const string &value);
~HashtableItem();
public:
const unsigned int 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 unsigned int key, const string &value);
friend Hashtable::~Hashtable();
friend void Hashtable::Remove(const unsigned int key);
friend void Hashtable::Clear();
friend HashtableItem *Hashtable::operator[](const unsigned int key) const;
friend HashtableItem *Hashtable::GetNext();
};
// ##################### 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 unsigned int key, const string &value)
{
unsigned int i = HASH_MACRO(key, 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 unsigned int key) const
{
unsigned int i = HASH_MACRO(key, 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 unsigned int key)
{
unsigned int i = HASH_MACRO(key, 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 unsigned int xKey, const string &xValue) : key(xKey), value(xValue), pnext(nullptr) {}
HashtableItem::~HashtableItem()
{
if (this->pnext)
delete this->pnext;
}
const unsigned int 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[])
{
unsigned int key;
string value;
Hashtable ht;
HashtableItem *item;
while (true)
{
cout << "enter a key (whole number) for the hash (-1 to quit): ";
cin >> key;
if ((int)key == -1)
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 (whole number) to delete (-1 to quit): ";
cin >> key;
if ((int)key == -1)
break;
ht.Remove(key);
}
return 0;
}
To convert this class to use C++ templates click here. To see the string implementation of this class click here.
Comment