experimenting on a simpler HashTable implementation

This commit is contained in:
hyung-hwan 2015-02-25 16:27:46 +00:00
parent 37792be000
commit f93f5e9704
3 changed files with 333 additions and 29 deletions

View File

@ -64,26 +64,49 @@ struct HashListResizer
} }
}; };
/// The HashList class provides a linked list where a data item can be accessed
/// using hashing. The accessor functions are similar to those of the HashTable
/// class whereas the data items are linked with each other in a linked list.
/// Extra hashing buckets maintain pointers to the first and the last node of
/// data items whose hash values are equal. Unlike the HashTable class that
/// maintains pairs of a key and a value, it stores a series of single items
/// whose key is distinuguished via the hashing function.
///
/// For the capacicity of X, it allocates (X * 2) node slots(this->nodes).
/// For a hash value of hc, this->nodes[hc * 2] points to the first node and
/// this->nodes[hc * 2 + 1] ponits to the last node.
template <typename T, typename MPOOL = Mpool, typename HASHER = HashListHasher<T>, typename COMPARATOR = HashListComparator<T>, typename RESIZER = HashListResizer > template <typename T, typename MPOOL = Mpool, typename HASHER = HashListHasher<T>, typename COMPARATOR = HashListComparator<T>, typename RESIZER = HashListResizer >
class HashList: public Mmged class HashList: public Mmged
{ {
public: public:
typedef LinkedList<T,MPOOL> DatumList; typedef LinkedList<T,MPOOL> DatumList;
typedef typename DatumList::Node Node; typedef typename DatumList::Node Node;
typedef HashList<T,MPOOL,HASHER,COMPARATOR> SelfType; typedef typename DatumList::Iterator Iterator;
typedef typename DatumList::Visiter Visiter;
typedef HashList<T,MPOOL,HASHER,COMPARATOR,RESIZER> SelfType;
typedef HashListHasher<T> DefaultHasher; typedef HashListHasher<T> DefaultHasher;
typedef HashListComparator<T> DefaultComparator; typedef HashListComparator<T> DefaultComparator;
typedef HashListResizer DefaultResizer; typedef HashListResizer DefaultResizer;
enum
{
DEFAULT_CAPACITY = 10,
DEFAULT_LOAD_FACTOR = 75, // Load factor in percentage
MIN_CAPACITY = 1,
MIN_LOAD_FACTOR = 20
};
HashList ( HashList (
Mmgr* mmgr = QSE_NULL, Mmgr* mmgr = QSE_NULL,
qse_size_t node_capacity = 10, qse_size_t node_capacity = DEFAULT_CAPACITY,
qse_size_t load_factor = 75, qse_size_t load_factor = DEFAULT_LOAD_FACTOR,
qse_size_t mpb_size = 0): Mmged(mmgr) qse_size_t mpb_size = 0): Mmged(mmgr)
{ {
if (node_capacity <= 0) node_capacity = 1; if (node_capacity < MIN_CAPACITY) node_capacity = MIN_CAPACITY;
if (load_factor < 20) load_factor = 20; if (load_factor < MIN_LOAD_FACTOR) load_factor = MIN_LOAD_FACTOR;
this->nodes = QSE_NULL; this->nodes = QSE_NULL;
this->node_capacity = 0; this->node_capacity = 0;
@ -98,10 +121,6 @@ public:
//this->nodes = new Node*[total_count]; //this->nodes = new Node*[total_count];
this->nodes = (Node**)this->getMmgr()->allocate (QSE_SIZEOF(Node*) * total_count); this->nodes = (Node**)this->getMmgr()->allocate (QSE_SIZEOF(Node*) * total_count);
// NOTE: something wil go wrong if the memory manager doesn't raise an exception
// upon memory allocation failure. Make sure to use a memory allocation
// that raises an exception.
this->node_capacity = node_capacity; this->node_capacity = node_capacity;
for (qse_size_t i = 0; i < total_count; i++) for (qse_size_t i = 0; i < total_count; i++)
{ {
@ -447,6 +466,11 @@ public:
} }
} }
void traverse (Visiter& visiter)
{
return this->datum_list->traverse (visiter);
}
qse_size_t getCapacity() const qse_size_t getCapacity() const
{ {
return this->node_capacity; return this->node_capacity;
@ -457,6 +481,31 @@ public:
return this->datum_list->getSize(); return this->datum_list->getSize();
} }
/// The getIterator() function returns an interator.
///
/// \code
/// struct IntHasher
/// {
/// qse_size_t operator() (int v) { return v; }
/// };
/// typedef QSE::HashList<int,QSE::Mpool,IntHasher> IntList;
///
/// IntList hl;
/// IntList::Iterator it;
///
/// hl.insert (10);
/// hl.insert (150);
/// hl.insert (200);
/// for (it = hl.getIterator(); it.isLegit(); it++)
/// {
/// printf ("%d\n", *it);
/// }
/// \endcode
Iterator getIterator (qse_size_t index = 0)
{
return this->datum_list->getIterator (index);
}
protected: protected:
mutable qse_size_t node_capacity; mutable qse_size_t node_capacity;
mutable Node** nodes; mutable Node** nodes;
@ -577,7 +626,6 @@ private:
} }
}; };
///////////////////////////////// /////////////////////////////////
QSE_END_NAMESPACE(QSE) QSE_END_NAMESPACE(QSE)
///////////////////////////////// /////////////////////////////////

View File

@ -27,9 +27,11 @@
#ifndef _QSE_CMN_HASHTABLE_HPP_ #ifndef _QSE_CMN_HASHTABLE_HPP_
#define _QSE_CMN_HASHTABLE_HPP_ #define _QSE_CMN_HASHTABLE_HPP_
#include <qse/Hashable.hpp> /*#include <qse/Hashable.hpp>
#include <qse/cmn/LinkedList.hpp> #include <qse/cmn/LinkedList.hpp>*/
#include <qse/cmn/Couple.hpp> #include <qse/cmn/Couple.hpp>
#include <qse/cmn/HashList.hpp>
///////////////////////////////// /////////////////////////////////
QSE_BEGIN_NAMESPACE(QSE) QSE_BEGIN_NAMESPACE(QSE)
@ -54,6 +56,7 @@ struct HashTableComparator
} }
}; };
#if 0
struct HashTableResizer struct HashTableResizer
{ {
qse_size_t operator() (qse_size_t current) const qse_size_t operator() (qse_size_t current) const
@ -65,7 +68,118 @@ struct HashTableResizer
(current + (current / 16)); (current + (current / 16));
} }
}; };
#endif
typedef HashListResizer HashTableResizer;
template <typename K, typename V, typename MPOOL = Mpool, typename HASHER = HashTableHasher<K>, typename COMPARATOR = HashTableComparator<K>, typename RESIZER = HashTableResizer>
class HashTable: public Mmged
{
public:
typedef Couple<K,V> Pair;
typedef HashTable<K,V,MPOOL,HASHER,COMPARATOR,RESIZER> SelfType;
typedef HashTableHasher<K> DefaultHasher;
typedef HashTableComparator<K> DefaultComparator;
typedef HashTableResizer DefaultResizer;
struct PairHasher
{
qse_size_t operator() (const Pair& p)
{
HASHER hasher;
return hasher (p.key);
}
};
struct PairComparator
{
qse_size_t operator() (const Pair& p1, const Pair& p2)
{
COMPARATOR comparator;
return comparator (p1.key, p2.key);
}
};
typedef HashList<Pair,MPOOL,PairHasher,PairComparator,RESIZER> PairList;
typedef typename PairList::Node PairNode;
enum
{
DEFAULT_CAPACITY = PairList::DEFAULT_CAPACITY,
DEFAULT_LOAD_FACTOR = PairList::DEFAULT_LOAD_FACTOR,
MIN_CAPACITY = PairList::MIN_CAPACITY,
MIN_LOAD_FACTOR = PairList::MIN_LOAD_FACTOR
};
HashTable (Mmgr* mmgr, qse_size_t capacity = DEFAULT_CAPACITY, qse_size_t load_factor = DEFAULT_LOAD_FACTOR, qse_size_t mpb_size = 0): Mmged(mmgr), pair_list (mmgr, capacity, load_factor, mpb_size)
{
}
HashTable (const SelfType& table): Mmged (table), pair_list (table.pair_list)
{
}
SelfType& operator= (const SelfType& table)
{
this->pair_list = table.pair_list;
return *this;
}
Pair* insert (const K& key, const V& value)
{
PairNode* node = this->pair_list.insert (Pair(key, value));
if (!node) return QSE_NULL;
return &node->value;
}
Pair* upsert (const K& key, const V& value)
{
PairNode* node = this->pair_list.upsert (Pair(key, value));
if (!node) return QSE_NULL;
return &node->value;
}
Pair* update (const K& key, const V& value)
{
PairNode* node = this->pair_list.update (Pair(key, value));
if (!node) return QSE_NULL;
return &node->value;
}
Pair* search (const K& key)
{
// TODO: find with custom...
PairNode* node = this->pair_list.update (Pair(key));
if (!node) return QSE_NULL;
return &node->value;
}
int remove (const K& key)
{
// TODO: use removeWithCustom....
return this->pair_list.remove (Pair(key));
}
void clear ()
{
// TODO: accept new capacity.
return this->pair_list.clear ();
}
qse_size_t getSize() const
{
return this->pair_list.getSize ();
}
protected:
PairList pair_list;
};
#if 0
template <typename K, typename V, typename HASHER = HashTableHasher<K>, typename COMPARATOR = HashTableComparator<K>, typename RESIZER = HashTableResizer> template <typename K, typename V, typename HASHER = HashTableHasher<K>, typename COMPARATOR = HashTableComparator<K>, typename RESIZER = HashTableResizer>
class HashTable: public Mmged class HashTable: public Mmged
{ {
@ -79,8 +193,101 @@ public:
typedef HashTableComparator<K> DefaultComparator; typedef HashTableComparator<K> DefaultComparator;
typedef HashTableResizer DefaultResizer; typedef HashTableResizer DefaultResizer;
enum
{
DEFAULT_CAPACITY = 10,
DEFAULT_LOAD_FACTOR = 75, // Load factor in percentage
MIN_CAPACITY = 1,
MIN_LOAD_FACTOR = 20
};
#if 0
class Iterator
{
public:
Iterator (): bucket_index(0), bucket_node(QSE_NULL) {}
Iterator (qse_size_t index, BucketNode* node): bucket_index(index), bucket_node(node) {}
Iterator (const Iterator& it): bucket_index (it.bucket_index), bucket_node(it.bucket_node) {}
Iterator& operator= (const Iterator& it)
{
this->bucket_index = it.bucket_index;
this->bucket_node = it.bucket_node;
return *this;
}
Iterator& operator++ () // prefix increment
{
QSE_ASSERT (this->isLegit());
this->bucket_node = this->bucket_node->getNext();
if (!this->bucket_node)
{
while (this->bucket_index
}
return *this;
}
Iterator operator++ (int) // postfix increment
{
QSE_ASSERT (this->isLegit());
Iterator saved (*this);
this->current = this->current->getNext(); //++(*this);
return saved;
}
Iterator& operator-- () // prefix decrement
{
QSE_ASSERT (this->isLegit());
this->current = this->current->getPrev();
return *this;
}
Iterator operator-- (int) // postfix decrement
{
QSE_ASSERT (this->isLegit());
Iterator saved (*this);
this->current = this->current->getPrev(); //--(*this);
return saved;
}
bool operator== (const Iterator& it) const
{
return this->bucket_index == it.bucket_index &&
this->bucket_node == it.bucket_node;
}
bool operator!= (const Iterator& it) const
{
return this->bucket_index != it.bucket_index ||
this->bucket_node != it.bucket_node;
}
bool isLegit () const
{
// TODO: change this
return this->bucket_node != QSE_NULL;
}
T& operator* () // dereference
{
return this->bucket_node->getValue();
}
const T& operator* () const // dereference
{
return this->bucket_node->getValue();
}
protected:
SelfType* table;
qse_size_t bucket_index;
BucketNode* bucket_node;
};
#endif
protected: protected:
Bucket** allocate_bucket (Mmgr* mm, qse_size_t bs) const Bucket** allocate_bucket (Mmgr* mm, qse_size_t bs, qse_size_t mpb_size) const
{ {
Bucket** b = QSE_NULL; Bucket** b = QSE_NULL;
@ -89,7 +296,7 @@ protected:
b = (Bucket**) mm->callocate (QSE_SIZEOF(*b) * bs); b = (Bucket**) mm->callocate (QSE_SIZEOF(*b) * bs);
for (qse_size_t i = 0; i < bs; i++) for (qse_size_t i = 0; i < bs; i++)
{ {
b[i] = new(mm) Bucket (mm, this->bucket_mpb_size); b[i] = new(mm) Bucket (mm, mpb_size);
} }
} }
catch (...) catch (...)
@ -129,9 +336,12 @@ protected:
} }
public: public:
HashTable (Mmgr* mmgr, qse_size_t bucket_size = 10, qse_size_t load_factor = 75, qse_size_t bucket_mpb_size = 0): Mmged(mmgr) HashTable (Mmgr* mmgr, qse_size_t bucket_size = DEFAULT_CAPACITY, qse_size_t load_factor = DEFAULT_LOAD_FACTOR, qse_size_t bucket_mpb_size = 0): Mmged(mmgr)
{ {
this->buckets = this->allocate_bucket (this->getMmgr(), bucket_size); if (bucket_size < MIN_CAPACITY) bucket_size = MIN_CAPACITY;
if (load_factor < MIN_LOAD_FACTOR) load_factor = MIN_LOAD_FACTOR;
this->buckets = this->allocate_bucket (this->getMmgr(), bucket_size, bucket_mpb_size);
this->bucket_size = bucket_size; this->bucket_size = bucket_size;
this->pair_count = 0; this->pair_count = 0;
this->load_factor = load_factor; this->load_factor = load_factor;
@ -141,7 +351,7 @@ public:
HashTable (const SelfType& table): Mmged (table) HashTable (const SelfType& table): Mmged (table)
{ {
this->buckets = this->allocate_bucket (this->getMmgr(), table.bucket_size); this->buckets = this->allocate_bucket (this->getMmgr(), table.bucket_size, table.bucket_mpb_size);
this->bucket_size = table.bucket_size; this->bucket_size = table.bucket_size;
this->pair_count = 0; this->pair_count = 0;
this->load_factor = table.load_factor; this->load_factor = table.load_factor;
@ -414,9 +624,9 @@ public:
} }
// insert a new pair // insert a new pair
Pair& new_pair = this->buckets[hc]->append (Pair(key)); BucketNode* node = this->buckets[hc]->append (Pair(key));
this->pair_count++; this->pair_count++;
return &new_pair; return &node->value;
} }
/// The insert() function inserts a new pair with a \a key with a \a value. /// The insert() function inserts a new pair with a \a key with a \a value.
@ -436,9 +646,9 @@ public:
} }
// insert a new pair // insert a new pair
Pair& new_pair = this->buckets[hc]->append (Pair(key, value)); BucketNode* node = this->buckets[hc]->append (Pair(key, value));
this->pair_count++; this->pair_count++;
return &new_pair; return &node->value;
} }
/// The update() function updates an existing pair of the \a key /// The update() function updates an existing pair of the \a key
@ -469,9 +679,9 @@ public:
} }
// insert a new pair if the key is not found // insert a new pair if the key is not found
Pair& new_pair = this->buckets[hc]->append (Pair(key)); BucketNode* node = this->buckets[hc]->append (Pair(key));
this->pair_count++; this->pair_count++;
return &new_pair; return &node->value;
} }
/// The upsert() function inserts a new pair with a \a key and a \a value /// The upsert() function inserts a new pair with a \a key and a \a value
@ -496,9 +706,9 @@ public:
} }
// insert a new pair if the key is not found // insert a new pair if the key is not found
Pair& new_pair = this->buckets[hc]->append (Pair(key, value)); BucketNode* node = this->buckets[hc]->append (Pair(key, value));
this->pair_count++; this->pair_count++;
return &new_pair; return &node->value;
} }
protected: protected:
@ -550,7 +760,7 @@ public:
if (new_bucket_size > 0) if (new_bucket_size > 0)
{ {
Bucket** tmp = this->allocate_bucket (this->getMmgr(), new_bucket_size); Bucket** tmp = this->allocate_bucket (this->getMmgr(), new_bucket_size, this->bucket_mpb_size);
this->dispose_bucket (this->getMmgr(), this->buckets, this->bucket_size); this->dispose_bucket (this->getMmgr(), this->buckets, this->bucket_size);
this->buckets = tmp; this->buckets = tmp;
@ -595,6 +805,10 @@ public:
return 0; return 0;
} }
//Iterator getIterator ()
//{
//}
protected: protected:
mutable qse_size_t pair_count; mutable qse_size_t pair_count;
mutable qse_size_t bucket_size; mutable qse_size_t bucket_size;
@ -609,7 +823,7 @@ protected:
void rehash () const void rehash () const
{ {
qse_size_t new_bucket_size = this->resizer (this->bucket_size); qse_size_t new_bucket_size = this->resizer (this->bucket_size);
Bucket** new_buckets = this->allocate_bucket (this->getMmgr(), new_bucket_size); Bucket** new_buckets = this->allocate_bucket (this->getMmgr(), new_bucket_size, this->bucket_mpb_size);
try try
{ {
@ -629,7 +843,9 @@ protected:
// and retains the previous pointers before rehashing. // and retains the previous pointers before rehashing.
// if the bucket uses a memory pool, this would not // if the bucket uses a memory pool, this would not
// work. fortunately, the hash table doesn't use it // work. fortunately, the hash table doesn't use it
// for a bucket. // for a bucket. ---> this is not true any more.
// ---> this has been broken as memory pool
// ---> can be activated for buckets.
BucketNode* np = this->buckets[i]->getHeadNode(); BucketNode* np = this->buckets[i]->getHeadNode();
while (np) while (np)
{ {
@ -654,6 +870,7 @@ protected:
this->threshold = this->load_factor * this->bucket_size / 100; this->threshold = this->load_factor * this->bucket_size / 100;
} }
}; };
#endif
///////////////////////////////// /////////////////////////////////
QSE_END_NAMESPACE(QSE) QSE_END_NAMESPACE(QSE)

View File

@ -138,7 +138,7 @@ public:
return this->current != it.current; return this->current != it.current;
} }
bool isValid () const bool isLegit () const
{ {
return this->current != QSE_NULL; return this->current != QSE_NULL;
} }
@ -211,6 +211,13 @@ public:
typedef Mpool DefaultMpool; typedef Mpool DefaultMpool;
typedef LinkedListComparator<T> DefaultComparator; typedef LinkedListComparator<T> DefaultComparator;
struct Visiter
{
// return 1 to move forward, -1 to move backward, 0 to stop
virtual ~Visiter() {}
virtual int operator() (Node* node) = 0;
};
enum enum
{ {
INVALID_INDEX = ~(qse_size_t)0 INVALID_INDEX = ~(qse_size_t)0
@ -315,6 +322,20 @@ public:
return node; return node;
} }
/// The prependNode() function adds an externally created \a node
/// to the front of the list.
Node* prependNode (Node* node)
{
return this->insertNode (this->head_node, node);
}
/// The appendNode() function adds an externally created \a node
/// to the back of the list.
Node* appendNode (Node* node)
{
return this->insertNode (QSE_NULL, node);
}
// create a new node to hold the value and insert it. // create a new node to hold the value and insert it.
Node* insert (Node* pos, const T& value) Node* insert (Node* pos, const T& value)
{ {
@ -643,6 +664,24 @@ public:
} }
} }
void traverse (Visiter& visiter)
{
Node* cur, * prev, * next;
cur = this->head_node;
while (cur)
{
prev = cur->prev;
next = cur->next;
int n = visiter (cur);
if (n > 0) cur = next;
else if (n < 0) cur = prev;
else break;
}
}
Iterator getIterator (qse_size_t index = 0) const Iterator getIterator (qse_size_t index = 0) const
{ {
if (this->node_count <= 0) if (this->node_count <= 0)