ORIGINAL
Loading...
Searching...
No Matches
hashTable.h
Go to the documentation of this file.
1#ifndef HASHTABLE_H
2#define HASHTABLE_H
3
4#include "allocator.h"
5#include "couple.h"
6#include "hash.h"
8#include "vector.h"
9#include "wrapper.h"
10
11
28
29namespace original {
30
54 template<typename K_TYPE, typename V_TYPE, typename ALLOC = allocator<K_TYPE>, typename HASH = hash<K_TYPE>>
55 class hashTable{
56 protected:
57
71 class hashNode final : public wrapper<couple<const K_TYPE, V_TYPE>> {
73 hashNode* next_;
74
75 public:
82 explicit hashNode(const K_TYPE& key = K_TYPE{}, const V_TYPE& value = V_TYPE{}, hashNode* next = nullptr);
83
88 hashNode(const hashNode& other);
89
95 hashNode& operator=(const hashNode& other);
96
102
107 const couple<const K_TYPE, V_TYPE>& getVal() const override;
108
113 const K_TYPE& getKey() const;
114
119 const V_TYPE& getValue() const;
120
125 V_TYPE& getValue();
126
130 void setVal(couple<const K_TYPE, V_TYPE> data) override;
131
136 void setValue(const V_TYPE& value);
137
141 hashNode* getPPrev() const override;
142
147 hashNode* getPNext() const override;
148
153 void setPNext(hashNode* new_next);
154
161 static void connect(hashNode* prev, hashNode* next);
162 };
163
168 using rebind_alloc_node = typename ALLOC::template rebind_alloc<hashNode>;
169
174 using rebind_alloc_pointer = typename ALLOC::template rebind_alloc<hashNode*>;
175
181
185 static constexpr floating LOAD_FACTOR_MIN = 0.25;
186
190 static constexpr floating LOAD_FACTOR_MAX = 0.75;
191
196 static constexpr u_integer BUCKETS_SIZES_COUNT = 30;
197
229 static constexpr u_integer BUCKETS_SIZES[] = {
230 17, 29, 53, 97, 193,
231 389, 769, 1543, 3079, 6151,
232 12289, 24593, 49157, 98317, 196613,
233
234 393241, 786433, 1572869, 3145739, 6291469,
235 12582917, 25165843, 50331653, 100663319, 201326611,
236
237 402653189, 805306457, 1610612741, 3221225473, 4294967291
238 };
239
240 u_integer size_;
241 buckets_type buckets;
242 HASH hash_;
243 mutable rebind_alloc_node rebind_alloc{};
244
261 class Iterator {
262 protected:
264 mutable u_integer cur_bucket;
265 mutable hashNode* p_node;
266
275 u_integer bucket);
276
285 u_integer bucket);
286
294 explicit Iterator(vector<hashNode*, rebind_alloc_pointer>* buckets = nullptr,
295 u_integer bucket = 0, hashNode* node = nullptr);
296
301 Iterator(const Iterator& other);
302
308 Iterator& operator=(const Iterator& other);
309 public:
314 [[nodiscard]] bool hasNext() const;
315
320 void next() const;
321
327 void operator+=(integer steps) const;
328
335
342
347 [[nodiscard]] bool isValid() const;
348 };
349
376 buckets_type bucketsCopy(const buckets_type& old_buckets) const;
377
386 hashNode* createNode(const K_TYPE& key = K_TYPE{}, const V_TYPE& value = V_TYPE{}, hashNode* next = nullptr) const;
387
393 void destroyNode(hashNode* node) noexcept;
394
400 u_integer getHashCode(const K_TYPE& key) const;
401
407
413 hashNode* getBucket(const K_TYPE& key) const;
414
419 floating loadFactor() const;
420
426 u_integer getNextSize() const;
427
432 u_integer getPrevSize() const;
433
443 void rehash(u_integer new_bucket_count);
444
451 void adjust();
452
457 explicit hashTable(HASH hash = HASH{});
458
464 hashNode* find(const K_TYPE& key) const;
465
472 bool modify(const K_TYPE& key, const V_TYPE& value);
473
481 bool insert(const K_TYPE& key, const V_TYPE& value);
482
489 bool erase(const K_TYPE& key);
490
495 ~hashTable();
496 };
497}
498
499template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
501 : data_({key, value}), next_(next) {}
502
503template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
507
508template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
511 if (this == &other)
512 return *this;
513 this->data_ = other.data_;
514 this->next_ = other.next_;
515 return *this;
516}
517
518template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
523
524template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
529
530template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
532 return this->getVal().first();
533}
534
535template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
537 return this->getVal().second();
538}
539
540template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
544
545template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
549
550template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
552 this->data_.template set<1>(value);
553}
554
555template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
560
561template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
566
567template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
571
572template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
574 if (prev != nullptr) prev->setPNext(next);
575}
576
577template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
581 for (u_integer i = bucket + 1; i < buckets->size(); i++) {
582 if ((*buckets)[i])
583 return i;
584 }
585 return buckets->size();
586}
587
588template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
591 vector<hashNode*, rebind_alloc_pointer> *buckets, const u_integer bucket) {
592 if (bucket == 0) return buckets->size();
593 for (u_integer i = bucket - 1; i > 0; i--) {
594 if ((*buckets)[i])
595 return i;
596 }
597 if ((*buckets)[0]) {
598 return 0;
599 }
600 return buckets->size();
601}
602
603template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
605 vector<hashNode *, rebind_alloc_pointer> *buckets, const u_integer bucket, hashNode *node)
606 : p_buckets(buckets), cur_bucket(bucket), p_node(node) {}
607
608template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
612
613template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
616 if (this == &other)
617 return *this;
618
619 this->p_buckets = other.p_buckets;
620 this->p_node = other.p_node;
621 return *this;
622}
623
624template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
626 if (this->p_node && this->p_node->getPNext()) {
627 return true;
628 }
629
630 return Iterator::findNextValidBucket(this->p_buckets, this->cur_bucket) != this->p_buckets->size();
631}
632
633template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
635 if (!this->isValid()) {
636 throw outOfBoundError();
637 }
638
639 if (this->p_node->getPNext()) {
640 this->p_node = this->p_node->getPNext();
641 return;
642 }
643
644 if (auto next_bucket = Iterator::findNextValidBucket(this->p_buckets, this->cur_bucket);
645 next_bucket != this->p_buckets->size()) {
646 this->cur_bucket = next_bucket;
647 this->p_node = this->p_buckets->get(next_bucket);
648 return;
649 }
650
651 this->cur_bucket = this->p_buckets->size();
652 this->p_node = nullptr;
653}
654
655template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
657 if (steps < 0) {
659 }
660
661 for (integer i = 0; i < steps; ++i) {
662 this->next();
663 }
664}
665
666template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
669 if (!this->isValid()) {
670 throw outOfBoundError();
671 }
672
673 return this->p_node->getVal();
674}
675
676template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
679 if (!this->isValid()) {
680 throw outOfBoundError();
681 }
682
683 return this->p_node->getVal();
684}
685
686template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
690
691template <typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
694{
695 buckets_type new_buckets = buckets_type(old_buckets.size(), rebind_alloc_pointer{}, nullptr);
696 for (u_integer i = 0; i < old_buckets.size(); ++i) {
697 hashNode* old_node = old_buckets[i];
698 hashNode* prev_new_node = nullptr;
699
700 while (old_node) {
701 hashNode* new_node = this->createNode(old_node->getKey(), old_node->getValue());
702
703 if (!prev_new_node) {
704 new_buckets[i] = new_node;
705 } else {
706 prev_new_node->setPNext(new_node);
707 }
708
709 prev_new_node = new_node;
710 old_node = old_node->getPNext();
711 }
712 }
713 return new_buckets;
714}
715
716template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
718original::hashTable<K_TYPE, V_TYPE, ALLOC, HASH>::createNode(const K_TYPE& key, const V_TYPE& value, hashNode* next) const {
719 auto node = this->rebind_alloc.allocate(1);
720 this->rebind_alloc.construct(node, key, value, next);
721 return node;
722}
723
724template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
726 this->rebind_alloc.destroy(node);
727 this->rebind_alloc.deallocate(node, 1);
728}
729
730template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
733 return this->hash_(key) % this->getBucketCount();
734}
735
736template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
739 return this->buckets.size();
740}
741
742template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
745 u_integer code = this->getHashCode(key);
746 return this->buckets[code];
747}
748
749template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
752 return static_cast<floating>(this->size_) / this->getBucketCount();
753}
754
755template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
757 for (u_integer i : BUCKETS_SIZES){
758 if (this->getBucketCount() < i){
759 return i;
760 }
761 }
762 throw outOfBoundError();
763}
764
765template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
767 const u_integer current = this->getBucketCount();
768 for (u_integer i = BUCKETS_SIZES_COUNT - 1; i > 0; --i){
769 if (BUCKETS_SIZES[i] < current){
770 return BUCKETS_SIZES[i];
771 }
772 }
773 return BUCKETS_SIZES[0];
774}
775
776template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
778 if (new_bucket_count == this->getBucketCount())
779 return;
780
781 auto new_buckets = buckets_type(new_bucket_count, rebind_alloc_pointer{}, nullptr);
782
783 for (hashNode*& old_head : this->buckets) {
784 while (old_head) {
785 hashNode* cur = old_head;
786 old_head = old_head->getPNext();
787
788 cur->setPNext(nullptr);
789 auto code = this->hash_(cur->getKey()) % new_bucket_count;
790
791 cur->setPNext(new_buckets[code]);
792 new_buckets[code] = cur;
793 }
794 }
795
796 this->buckets = std::move(new_buckets);
797}
798
799template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
801 if (this->loadFactor() <= LOAD_FACTOR_MIN){
802 this->rehash(this->getPrevSize());
803 } else if (this->loadFactor() >= LOAD_FACTOR_MAX){
804 this->rehash(this->getNextSize());
805 }
806}
807
808template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
813
814template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
817 if (this->size_ == 0)
818 return nullptr;
819
820 for (auto cur = this->getBucket(key); cur; cur = cur->getPNext()){
821 if (cur->getKey() == key)
822 return cur;
823 }
824 return nullptr;
825}
826
827template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
828bool original::hashTable<K_TYPE, V_TYPE, ALLOC, HASH>::modify(const K_TYPE &key, const V_TYPE &value) {
829 if (auto cur = this->find(key)){
830 cur->setValue(value);
831 return true;
832 }
833 return false;
834}
835
836template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
837bool original::hashTable<K_TYPE, V_TYPE, ALLOC, HASH>::insert(const K_TYPE &key, const V_TYPE &value) {
838 this->adjust();
839
840 auto cur = this->getBucket(key);
841 if (!cur){
842 this->buckets[this->getHashCode(key)] = this->createNode(key, value);
843 } else{
844 if (cur->getKey() == key)
845 return false;
846
847 for (; cur->getPNext(); cur = cur->getPNext()){
848 if (cur->getKey() == key)
849 return false;
850 }
851 hashNode::connect(cur, this->createNode(key, value));
852 }
853
854 this->size_ += 1;
855 return true;
856}
857
858template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
860 if (this->size_ == 0)
861 return false;
862
863 this->adjust();
864
865 u_integer code = this->getHashCode(key);
866 hashNode* cur = this->buckets[code];
867 hashNode* prev = nullptr;
868
869 while (cur){
870 if (cur->getKey() == key) {
871 if (prev) {
872 hashNode::connect(prev, cur->getPNext());
873 } else {
874 this->buckets[code] = cur->getPNext();
875 }
876 this->destroyNode(cur);
877 this->size_ -= 1;
878 return true;
879 }
880 prev = cur;
881 cur = cur->getPNext();
882 }
883
884 return false;
885}
886
887template<typename K_TYPE, typename V_TYPE, typename ALLOC, typename HASH>
889 for (hashNode*& bucket: this->buckets) {
890 while (bucket){
891 auto next = bucket->getPNext();
892 this->destroyNode(bucket);
893 bucket = next;
894 }
895 }
896}
897
898#endif //HASHTABLE_H
Memory allocation interface and implementations.
Container for two heterogeneous elements.
Definition couple.h:37
Forward declaration of hash class template.
Definition hash.h:80
Forward iterator for hashTable.
Definition hashTable.h:261
void next() const
Advances to the next element.
Definition hashTable.h:634
couple< const K_TYPE, V_TYPE > & get()
Gets current key-value pair (non-const)
Definition hashTable.h:668
Iterator & operator=(const Iterator &other)
Copy assignment operator.
Definition hashTable.h:615
static u_integer findPrevValidBucket(vector< hashNode *, rebind_alloc_pointer > *buckets, u_integer bucket)
Finds the previous non-empty bucket.
Definition hashTable.h:590
void operator+=(integer steps) const
Advances iterator by steps positions.
Definition hashTable.h:656
bool isValid() const
Checks if iterator points to valid element.
Definition hashTable.h:687
bool hasNext() const
Checks if more elements are available.
Definition hashTable.h:625
Iterator(vector< hashNode *, rebind_alloc_pointer > *buckets=nullptr, u_integer bucket=0, hashNode *node=nullptr)
Constructs an iterator pointing to specific position.
Definition hashTable.h:604
static u_integer findNextValidBucket(vector< hashNode *, rebind_alloc_pointer > *buckets, u_integer bucket)
Finds the next non-empty bucket.
Definition hashTable.h:579
Internal node type for hash table storage.
Definition hashTable.h:71
hashNode * getPNext() const override
Gets next node in chain.
Definition hashTable.h:563
couple< const K_TYPE, V_TYPE > & getVal() override
Gets key-value pair (non-const)
Definition hashTable.h:520
const V_TYPE & getValue() const
Gets the value (const)
Definition hashTable.h:536
hashNode * getPPrev() const override
Not supported (throws unSupportedMethodError)
Definition hashTable.h:557
void setPNext(hashNode *new_next)
Sets next node in chain.
Definition hashTable.h:568
const K_TYPE & getKey() const
Gets the key (const)
Definition hashTable.h:531
hashNode(const K_TYPE &key=K_TYPE{}, const V_TYPE &value=V_TYPE{}, hashNode *next=nullptr)
Constructs a new hash node.
Definition hashTable.h:500
void setValue(const V_TYPE &value)
Sets a new value.
Definition hashTable.h:551
static void connect(hashNode *prev, hashNode *next)
Connects two nodes in chain.
Definition hashTable.h:573
void setVal(couple< const K_TYPE, V_TYPE > data) override
Not supported (throws unSupportedMethodError)
Definition hashTable.h:546
hashNode & operator=(const hashNode &other)
Copy assignment operator.
Definition hashTable.h:510
hashNode * createNode(const K_TYPE &key=K_TYPE{}, const V_TYPE &value=V_TYPE{}, hashNode *next=nullptr) const
Creates a new hash node.
Definition hashTable.h:718
bool modify(const K_TYPE &key, const V_TYPE &value)
Modifies value for existing key.
Definition hashTable.h:828
typename ALLOC::template rebind_alloc< hashNode * > rebind_alloc_pointer
Rebound allocator type for pointer storage.
Definition hashTable.h:174
~hashTable()
Destroys hashTable.
Definition hashTable.h:888
u_integer getPrevSize() const
Gets previous appropriate bucket size for shrinking.
Definition hashTable.h:766
static constexpr floating LOAD_FACTOR_MAX
Maximum load factor before expanding.
Definition hashTable.h:190
vector< hashNode *, rebind_alloc_pointer > buckets_type
Type representing the hash table buckets container.
Definition hashTable.h:180
u_integer getHashCode(const K_TYPE &key) const
Computes hash code for a key.
Definition hashTable.h:732
static constexpr u_integer BUCKETS_SIZES_COUNT
Size of BUCKETS_SIZES BUCKETS_SIZES.
Definition hashTable.h:196
static constexpr floating LOAD_FACTOR_MIN
Minimum load factor before shrinking.
Definition hashTable.h:185
void rehash(u_integer new_bucket_count)
Rehashes table to new bucket count.
Definition hashTable.h:777
typename ALLOC::template rebind_alloc< hashNode > rebind_alloc_node
Rebound allocator type for node storage.
Definition hashTable.h:168
u_integer getNextSize() const
Gets next appropriate bucket size for expansion.
Definition hashTable.h:756
buckets_type bucketsCopy(const buckets_type &old_buckets) const
Creates a deep copy of the hash table's bucket array.
Definition hashTable.h:693
bool insert(const K_TYPE &key, const V_TYPE &value)
Inserts new key-value pair.
Definition hashTable.h:837
hashNode * find(const K_TYPE &key) const
Finds node for given key.
Definition hashTable.h:816
hashTable(HASH hash=HASH{})
Constructs empty hashTable.
Definition hashTable.h:809
hashNode * getBucket(const K_TYPE &key) const
Gets bucket head for a key.
Definition hashTable.h:744
void destroyNode(hashNode *node) noexcept
Destroys a hash node.
Definition hashTable.h:725
void adjust()
Adjusts table size based on load factor.
Definition hashTable.h:800
floating loadFactor() const
Calculates current load factor.
Definition hashTable.h:751
bool erase(const K_TYPE &key)
Removes key-value pair.
Definition hashTable.h:859
u_integer getBucketCount() const
Gets current number of buckets.
Definition hashTable.h:738
static constexpr u_integer BUCKETS_SIZES[]
Predefined bucket sizes for hash table resizing.
Definition hashTable.h:229
Exception for container index out-of-range errors.
Definition error.h:84
Abstract base class for unique element containers.
Definition set.h:44
Exception for unimplemented method calls.
Definition error.h:123
Dynamic array container with amortized constant time operations.
Definition vector.h:43
u_integer size() const override
Gets the size of the vector.
Definition vector.h:640
Base class for linked value containers with formatted output.
Definition wrapper.h:28
Generic pair container implementation.
Provides a generic hashing utility and a base interface for hashable types.
Main namespace for the project Original.
Definition algorithms.h:21
std::uint32_t u_integer
32-bit unsigned integer type for sizes and indexes
Definition config.h:43
double floating
Double-precision floating-point type.
Definition config.h:51
std::int64_t integer
64-bit signed integer type for arithmetic operations
Definition config.h:35
Single-direction iterator base class.
Dynamic array container with automatic resizing.
Abstract polymorphic container with value encapsulation and navigation support.