NexusForce 1.0.0
A Modern C++ Library with extended functionality, web components, and utility libraries
载入中...
搜索中...
未找到
hashtable.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_CONTAINER_HASHTABLE_HPP__
2#define NEFORCE_CORE_CONTAINER_HASHTABLE_HPP__
3
13
16NEFORCE_BEGIN_NAMESPACE__
17
23
31template <typename T>
42
43
44template <typename Value, typename Key, typename HashFcn, typename ExtractKey, typename EqualKey, typename Alloc>
45class hashtable;
46
47
56template <bool IsConst, typename HashTable>
57struct hashtable_iterator : iiterator<hashtable_iterator<IsConst, HashTable>> {
58public:
59 using container_type = HashTable;
60 using value_type = typename container_type::value_type;
61 using size_type = typename container_type::size_type;
62 using difference_type = typename container_type::difference_type;
64 using reference = conditional_t<IsConst, typename container_type::const_reference,
65 typename container_type::reference>;
66 using pointer = conditional_t<IsConst, typename container_type::const_pointer,
67 typename container_type::pointer>;
68
69private:
70 using node_type = hashtable_node<value_type>;
71
72 node_type* current_ = nullptr;
73 size_type bucket_ = 0;
74 const container_type* container_ = nullptr;
75
76 template <typename, typename, typename, typename, typename, typename>
77 friend class hashtable;
78
79public:
80 hashtable_iterator() noexcept = default;
81 ~hashtable_iterator() = default;
82
83 hashtable_iterator(const hashtable_iterator&) noexcept = default;
84 hashtable_iterator& operator=(const hashtable_iterator&) noexcept = default;
85 hashtable_iterator(hashtable_iterator&&) noexcept = default;
86 hashtable_iterator& operator=(hashtable_iterator&&) noexcept = default;
87
94 hashtable_iterator(node_type* ptr, const size_type bucket, const HashTable* ht) :
95 current_(ptr),
96 bucket_(bucket),
97 container_(ht) {}
98
103 NEFORCE_NODISCARD reference dereference() const noexcept {
104 NEFORCE_DEBUG_VERIFY(current_ && container_, "Attempting to dereference on a null pointer");
105 NEFORCE_DEBUG_VERIFY(bucket_ < container_->buckets_.size() && 0 <= bucket_,
106 "Attempting to dereference out of boundary");
107 return current_->data;
108 }
109
115 void increment() noexcept {
116 NEFORCE_DEBUG_VERIFY(current_ && container_, "Attempting to increment a null pointer");
117 NEFORCE_DEBUG_VERIFY(bucket_ < container_->buckets_.size() &&
118 !(bucket_ + 1 == container_->buckets_.size() && current_->next != nullptr),
119 "Attempting to increment out of boundary");
120 current_ = current_->next;
121 if (current_ == nullptr) {
122 while (current_ == nullptr && ++bucket_ < container_->buckets_.size()) {
123 current_ = container_->buckets_[bucket_];
124 }
125 }
126 }
127
133 NEFORCE_NODISCARD bool equal(const hashtable_iterator& rhs) const noexcept {
134 NEFORCE_DEBUG_VERIFY(container_ == rhs.container_, "Attempting to equal to a different container");
135 return current_ == rhs.current_;
136 }
137
142 NEFORCE_NODISCARD pointer base() const noexcept { return current_; }
143
148 NEFORCE_NODISCARD const container_type* container() const noexcept { return container_; }
149};
150
151
152NEFORCE_BEGIN_CONSTANTS__
153#ifdef NEFORCE_ARCH_BITS_64
154
160NEFORCE_INLINE17 constexpr size_t HASH_PRIME_LIST[] = {101,
161 173,
162 263,
163 397,
164 599,
165 907,
166 1361,
167 2053,
168 3083,
169 4637,
170 6959,
171 10453,
172 15683,
173 23531,
174 35311,
175 52967,
176 79451,
177 119179,
178 178781,
179 268189,
180 402299,
181 603457,
182 905189,
183 1357787,
184 2036687,
185 3055043,
186 4582577,
187 6873871,
188 10310819,
189 15466229,
190 23199347,
191 34799021,
192 52198537,
193 78297827,
194 117446801,
195 176170229,
196 264255353,
197 396383041,
198 594574583,
199 891861923,
200 1337792887,
201 2006689337,
202 3010034021u,
203 4515051137ull,
204 6772576709ull,
205 10158865069ull,
206 15238297621ull,
207 22857446471ull,
208 34286169707ull,
209 51429254599ull,
210 77143881917ull,
211 115715822899ull,
212 173573734363ull,
213 260360601547ull,
214 390540902329ull,
215 585811353559ull,
216 878717030339ull,
217 1318075545511ull,
218 1977113318311ull,
219 2965669977497ull,
220 4448504966249ull,
221 6672757449409ull,
222 10009136174239ull,
223 15013704261371ull,
224 22520556392057ull,
225 33780834588157ull,
226 50671251882247ull,
227 76006877823377ull,
228 114010316735089ull,
229 171015475102649ull,
230 256523212653977ull,
231 384784818980971ull,
232 577177228471507ull,
233 865765842707309ull,
234 1298648764060979ull,
235 1947973146091477ull,
236 2921959719137273ull,
237 4382939578705967ull,
238 6574409368058969ull,
239 9861614052088471ull,
240 14792421078132871ull,
241 22188631617199337ull,
242 33282947425799017ull,
243 49924421138698549ull,
244 74886631708047827ull,
245 112329947562071807ull,
246 168494921343107851ull,
247 252742382014661767ull,
248 379113573021992729ull,
249 568670359532989111ull,
250 853005539299483657ull,
251 1279508308949225477ull,
252 1919262463423838231ull,
253 2878893695135757317ull,
254 4318340542703636011ull,
255 6477510814055453699ull};
256
257#else
258
262NEFORCE_INLINE17 constexpr size_t HASH_PRIME_LIST[] = {
263 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289,
264 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
265 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};
266
267#endif
268
270NEFORCE_INLINE17 constexpr size_t HASH_PRIMER_COUNT = extent_v<decltype(HASH_PRIME_LIST)>;
271
272NEFORCE_END_CONSTANTS__
273
274
288template <typename Value, typename Key, typename HashFcn, typename ExtractKey, typename EqualKey, typename Alloc>
289class hashtable : public icollector<hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>> {
290public:
291 using key_type = Key;
292 using hasher = HashFcn;
293 using key_equal = EqualKey;
294
295 using value_type = Value;
296 using pointer = Value*;
297 using reference = Value&;
298 using const_pointer = const Value*;
299 using const_reference = const Value&;
302 using iterator = hashtable_iterator<false, hashtable>;
303 using const_iterator = hashtable_iterator<true, hashtable>;
304 using allocator_type = Alloc;
305
306private:
307 using node_type = hashtable_node<Value>;
308 using link_type = node_type*;
309
310 vector<link_type> buckets_{};
311 size_type size_ = 0;
312 hasher hasher_{};
313 key_equal equals_{};
314 ExtractKey extracter_{};
315 compressed_pair<allocator_type, float> pair_{default_construct_tag{}, 1.0f};
316
317 template <bool, typename>
318 friend struct hashtable_iterator;
319
320private:
326 NEFORCE_NODISCARD static size_type next_size(const size_type n) noexcept {
327 const size_t* first = constants::HASH_PRIME_LIST;
328 const size_t* last = constants::HASH_PRIME_LIST + constants::HASH_PRIMER_COUNT;
329 const size_t* pos = _NEFORCE lower_bound(first, last, n);
330 return pos == last ? *(last - 1) : *pos;
331 }
332
337 void initialize_buckets(const size_type n) {
338 const size_type n_buckets = next_size(n);
339 buckets_.assign(n_buckets, nullptr);
340 }
341
348 size_type bucket_index_key(const key_type& key, const size_t n) const noexcept(is_nothrow_hashable_v<key_type>) {
349 if (n == 0) {
350 return 0;
351 }
352 return hasher_(key) % n;
353 }
354
361 size_type bucket_index_value(const value_type& value, const size_t n) const
363 return hashtable::bucket_index_key(extracter_(value), n);
364 }
365
372 template <typename... Args>
373 link_type new_node(Args&&... args) {
374 link_type n = pair_.get_base().allocate();
375 n->next = nullptr;
376 try {
377 _NEFORCE construct(&n->data, _NEFORCE forward<Args>(args)...);
378 } catch (...) {
379 hashtable::delete_node(n);
380 NEFORCE_THROW_EXCEPTION(memory_exception("hashtable construct node failed."));
381 }
382 return n;
383 }
384
389 void delete_node(link_type n) noexcept {
390 _NEFORCE destroy(&n->data);
391 pair_.get_base().deallocate(n);
392 }
393
398 void copy_from(const hashtable& other) {
399 buckets_.clear();
400 buckets_.reserve(other.buckets_.size());
401 buckets_.insert(buckets_.end(), other.buckets_.size(), nullptr);
402 try {
403 for (size_type i = 0; i < other.buckets_.size(); ++i) {
404 if (link_type cur = other.buckets_[i]) {
405 link_type copy = hashtable::new_node(cur->data);
406 buckets_[i] = copy;
407 for (link_type next = cur->next; next != nullptr; cur = next, next = cur->next) {
408 copy->next = hashtable::new_node(next->data);
409 copy = copy->next;
410 }
411 }
412 }
413 size_ = other.size_;
414 } catch (...) {
415 clear();
416 throw;
417 }
418 }
419
425 pair<iterator, bool> insert_unique_noresize(link_type ptr) {
426 const size_type n = hashtable::bucket_index_value(ptr->data, buckets_.size());
427
428 node_type** buckets_ptr = &buckets_[n];
429 while (*buckets_ptr != nullptr) {
430 if (equals_(extracter_((*buckets_ptr)->data), extracter_(ptr->data))) {
431 ptr->next = (*buckets_ptr)->next;
432 hashtable::delete_node(*buckets_ptr);
433 *buckets_ptr = ptr;
434 return {{ptr, n, this}, false};
435 }
436 buckets_ptr = &(*buckets_ptr)->next;
437 }
438 ptr->next = nullptr;
439 *buckets_ptr = ptr;
440 ++size_;
441 return {iterator{ptr, n, this}, true};
442 }
443
449 iterator insert_equal_noresize(link_type ptr) {
450 const size_type n = hashtable::bucket_index_value(ptr->data, buckets_.size());
451 link_type first = buckets_[n];
452
453 link_type prev = nullptr;
454 link_type cur = first;
455 while (cur != nullptr && equals_(extracter_(cur->data), extracter_(ptr->data))) {
456 prev = cur;
457 cur = cur->next;
458 }
459
460 if (prev != nullptr) {
461 prev->next = ptr;
462 ptr->next = cur;
463 } else {
464 ptr->next = first;
465 buckets_[n] = ptr;
466 }
467
468 ++size_;
469 return {ptr, n, this};
470 }
471
478 template <typename Iterator>
479 enable_if_t<!is_ranges_fwd_iter_v<Iterator>> insert_unique_aux(Iterator first, Iterator last) {
480 for (; first != last; ++first) {
482 }
483 return;
484 }
485
492 template <typename Iterator>
493 enable_if_t<is_ranges_fwd_iter_v<Iterator>> insert_unique_aux(Iterator first, Iterator last) {
494 size_type n = _NEFORCE distance(first, last);
495
496 const size_type need_buckets =
497 static_cast<size_type>(_NEFORCE ceil(static_cast<double>(size_ + n) / max_load_factor()));
498 rehash(need_buckets);
499
500 for (; n > 0; --n, ++first) {
501 link_type tmp = hashtable::new_node(*first);
502 hashtable::insert_unique_noresize(tmp);
503 }
504 return;
505 }
506
513 template <typename Iterator>
514 enable_if_t<!is_ranges_fwd_iter_v<Iterator>> insert_equal_aux(Iterator first, Iterator last) {
515 for (; first != last; ++first) {
517 }
518 return;
519 }
520
527 template <typename Iterator>
528 enable_if_t<is_ranges_fwd_iter_v<Iterator>> insert_equal_aux(Iterator first, Iterator last) {
529 size_type n = _NEFORCE distance(first, last);
530
531 const size_type need_buckets =
532 static_cast<size_type>(_NEFORCE ceil(static_cast<double>(size_ + n) / max_load_factor()));
533 rehash(need_buckets);
534
535 for (; n > 0; --n, ++first) {
536 link_type tmp = hashtable::new_node(*first);
537 hashtable::insert_equal_noresize(tmp);
538 }
539 return;
540 }
541
548 size_type erase_bucket_to_node(size_type bucket, link_type last) noexcept {
549 size_type count = 0;
550 link_type curr = buckets_[bucket];
551 while (curr != nullptr && curr != last) {
552 link_type next = curr->next;
553 hashtable::delete_node(curr);
554 curr = next;
555 --size_;
556 ++count;
557 }
558 buckets_[bucket] = last;
559 return count;
560 }
561
569 size_type erase_bucket_range(size_type bucket, link_type first, link_type last) noexcept {
570 size_type count = 0;
571 if (first == nullptr) {
572 return 0;
573 }
574
575 if (buckets_[bucket] == first) {
576 count += hashtable::erase_bucket_to_node(bucket, last);
577 } else {
578 link_type prev = buckets_[bucket];
579 while (prev != nullptr && prev->next != first) {
580 prev = prev->next;
581 }
582 if (prev == nullptr) {
583 return 0;
584 }
585
586 link_type curr = first;
587 while (curr != nullptr && curr != last) {
588 link_type next = curr->next;
589 prev->next = next;
590 hashtable::delete_node(curr);
591 curr = next;
592 ++count;
593 }
594 }
595 return count;
596 }
597
603 size_type erase_bucket_completely(size_type bucket) noexcept {
604 size_type count = 0;
605 link_type curr = buckets_[bucket];
606 while (curr != nullptr) {
607 link_type next = curr->next;
608 hashtable::delete_node(curr);
609 curr = next;
610 ++count;
611 }
612 buckets_[bucket] = nullptr;
613 return count;
614 }
615
621 bool equal_small(const hashtable& rhs) const {
622 for (const_iterator iter = begin(); iter != end(); ++iter) {
623 const key_type& key = extracter_(*iter);
624
625 const size_t count_lhs = _NEFORCE count_if(
626 begin(), end(), [this, &key](const value_type& val) { return equals_(extracter_(val), key); });
627 const size_t count_rhs = _NEFORCE count_if(rhs.begin(), rhs.end(), [&rhs, &key](const value_type& val) {
628 return rhs.equals_(rhs.extracter_(val), key);
629 });
630
631 if (count_lhs != count_rhs) {
632 return false;
633 }
634 }
635 return true;
636 }
637
643 bool equal_large(const hashtable& rhs) const {
644 vector<value_type> elements_lhs, elements_rhs;
645 elements_lhs.reserve(size_);
646 elements_rhs.reserve(size_);
647
648 for (const_iterator it = begin(); it != end(); ++it) {
649 elements_lhs.push_back(*it);
650 }
651 for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) {
652 elements_rhs.push_back(*it);
653 }
654
655 _NEFORCE sort(elements_lhs.begin(), elements_lhs.end());
656 _NEFORCE sort(elements_rhs.begin(), elements_rhs.end());
657
658 return elements_lhs == elements_rhs;
659 }
660
661public:
667 explicit hashtable(const size_type n, float max_lf = 1.0f) :
668 buckets_(next_size(n), nullptr),
669 pair_(default_construct_tag{}, max_lf) {}
670
677 hashtable(const size_type n, const HashFcn& hf, float max_lf = 1.0f) :
678 buckets_(next_size(n), nullptr),
679 hasher_(hf),
680 pair_(default_construct_tag{}, max_lf) {}
681
689 hashtable(const size_type n, const HashFcn& hf, const EqualKey& eql, float max_lf = 1.0f) :
690 buckets_(next_size(n), nullptr),
691 hasher_(hf),
692 equals_(eql),
693 pair_(default_construct_tag{}, max_lf) {}
694
703 hashtable(const size_type n, const HashFcn& hf, const EqualKey& eql, const ExtractKey& ext, float max_lf = 1.0f) :
704 buckets_(next_size(n), nullptr),
705 hasher_(hf),
706 equals_(eql),
707 extracter_(ext),
708 pair_(default_construct_tag{}, max_lf) {}
709
714 hashtable(const hashtable& other) :
715 hasher_(other.hasher_),
716 equals_(other.equals_),
717 extracter_(other.extracter_),
718 pair_(other.pair_) {
719 hashtable::copy_from(other);
720 }
721
728 if (_NEFORCE addressof(other) == this) {
729 return *this;
730 }
732 hasher_ = other.hasher_;
733 equals_ = other.equals_;
734 extracter_ = other.extracter_;
735 hashtable::copy_from(other);
736 return *this;
737 }
738
743 hashtable(hashtable&& other) noexcept(noexcept(hashtable::swap(other))) :
744 buckets_(_NEFORCE move(other.buckets_)),
745 size_(other.size_),
746 hasher_(_NEFORCE move(other.hasher_)),
747 equals_(_NEFORCE move(other.equals_)),
748 extracter_(_NEFORCE move(other.extracter_)),
749 pair_(_NEFORCE move(other.pair_)) {
750 other.size_ = 0;
751 }
752
758 hashtable& operator=(hashtable&& other) noexcept(noexcept(hashtable::swap(other))) {
759 if (_NEFORCE addressof(other) == this) {
760 return *this;
761 }
762 clear();
763 hashtable::swap(other);
764 return *this;
765 }
766
771
776 NEFORCE_NODISCARD iterator begin() noexcept {
777 for (size_type n = 0; n < buckets_.size(); ++n) {
778 if (buckets_[n] != nullptr) {
779 return iterator(buckets_[n], n, this);
780 }
781 }
782 return end();
783 }
784
789 NEFORCE_NODISCARD iterator end() noexcept { return iterator(nullptr, 0, this); }
790
795 NEFORCE_NODISCARD const_iterator begin() const noexcept { return cbegin(); }
796
801 NEFORCE_NODISCARD const_iterator end() const noexcept { return cend(); }
802
807 NEFORCE_NODISCARD const_iterator cbegin() const noexcept {
808 for (size_type n = 0; n < buckets_.size(); ++n) {
809 if (buckets_[n] != nullptr) {
810 return const_iterator(buckets_[n], n, this);
811 }
812 }
813 return cend();
814 }
815
820 NEFORCE_NODISCARD const_iterator cend() const noexcept { return const_iterator(nullptr, 0, this); }
821
826 NEFORCE_NODISCARD size_type size() const noexcept { return size_; }
827
832 NEFORCE_NODISCARD size_type max_size() const noexcept { return static_cast<size_type>(-1); }
833
838 NEFORCE_NODISCARD bool empty() const noexcept { return size_ == 0; }
839
844 NEFORCE_NODISCARD size_type buckets_size() const noexcept { return buckets_.size(); }
845
850 NEFORCE_NODISCARD static size_type buckets_max_size() noexcept {
851 return constants::HASH_PRIME_LIST[constants::HASH_PRIMER_COUNT - 1];
852 }
853
859 NEFORCE_NODISCARD size_type bucket_index(const key_type& key) const noexcept(is_nothrow_hashable_v<key_type>) {
860 return hashtable::bucket_index_key(key);
861 }
862
868 NEFORCE_NODISCARD size_type bucket_size(size_type index) const noexcept {
869 size_type result = 0;
870 for (link_type cur = buckets_[index]; cur != nullptr; cur = cur->next) {
871 result++;
872 }
873 return result;
874 }
875
880 NEFORCE_NODISCARD hasher hash_func() const noexcept(is_nothrow_copy_constructible_v<hasher>) { return hasher_; }
881
886 NEFORCE_NODISCARD key_equal key_eql() const noexcept(is_nothrow_copy_constructible_v<key_equal>) { return equals_; }
887
892 NEFORCE_NODISCARD float load_factor() const noexcept {
893 return buckets_size() == 0 ? 0.0f : static_cast<float>(size()) / static_cast<float>(buckets_size());
894 }
895
900 NEFORCE_NODISCARD float max_load_factor() const noexcept { return pair_.value; }
901
906 void max_load_factor(const float lf) noexcept {
907 NEFORCE_DEBUG_VERIFY(lf > 0, "hashtable load factor invalid.");
908 pair_.value = lf;
909 }
910
915 void rehash(const size_type new_size) {
916 const auto min_buckets_for_size =
917 static_cast<size_type>(_NEFORCE ceil(static_cast<double>(size_) / max_load_factor()));
918 const size_type target = _NEFORCE max(new_size, min_buckets_for_size);
919 const size_type old_size = buckets_.size();
920
921 if (target <= old_size) {
922 return;
923 }
924
925 const size_type n = hashtable::next_size(target);
926 if (n < target) {
927 NEFORCE_THROW_EXCEPTION(value_exception("hashtable size exceeds max count"));
928 }
929
930 vector<link_type> new_buckets(n, nullptr);
931
932 for (size_type bucket = 0; bucket < old_size; ++bucket) {
933 link_type cur = buckets_[bucket];
934 while (cur != nullptr) {
935 link_type next = cur->next;
936 const size_type new_bucket = hashtable::bucket_index_value(cur->data, n);
937
938 cur->next = new_buckets[new_bucket];
939 new_buckets[new_bucket] = cur;
940
941 cur = next;
942 }
943 buckets_[bucket] = nullptr;
944 }
945
946 buckets_.swap(new_buckets);
947 }
948
955 void reserve(const size_type n) {
956 if (n <= size_) {
957 return;
958 }
959
960 const size_type needed = static_cast<size_type>(_NEFORCE ceil(static_cast<double>(n) / max_load_factor()));
961
962 if (needed > static_cast<float>(buckets_max_size())) {
963 NEFORCE_THROW_EXCEPTION(value_exception("hashtable size exceeds max count"));
964 }
965 rehash(static_cast<size_type>(needed));
966 }
967
974 template <typename... Args>
976 if (size_ + 1 > static_cast<size_type>(buckets_.size() * max_load_factor())) {
977 rehash(size_ + 1);
978 }
979 const link_type node = hashtable::new_node(_NEFORCE forward<Args>(args)...);
980 return hashtable::insert_unique_noresize(node);
981 }
982
989 template <typename... Args>
990 iterator emplace_equal(Args&&... args) {
991 if (size_ + 1 > static_cast<size_type>(buckets_.size() * max_load_factor())) {
992 rehash(size_ + 1);
993 }
994 const link_type node = hashtable::new_node(_NEFORCE forward<Args>(args)...);
995 return hashtable::insert_equal_noresize(node);
996 }
997
1004
1011
1018
1024 iterator insert_equal(value_type&& value) { return hashtable::emplace_equal(_NEFORCE move(value)); }
1025
1032 template <typename Iterator>
1033 enable_if_t<is_iter_v<Iterator>> insert_unique(Iterator first, Iterator last) {
1034 hashtable::insert_unique_aux(first, last);
1035 return;
1036 }
1037
1042 void insert_unique(std::initializer_list<value_type> ilist) {
1043 hashtable::insert_unique(ilist.begin(), ilist.end());
1044 }
1045
1052 template <typename Iterator>
1053 enable_if_t<is_iter_v<Iterator>> insert_equal(Iterator first, Iterator last) {
1054 hashtable::insert_equal_aux(first, last);
1055 return;
1056 }
1057
1062 void insert_equal(std::initializer_list<value_type> ilist) { hashtable::insert_equal(ilist.begin(), ilist.end()); }
1063
1070 const size_type n = hashtable::bucket_index_key(key, buckets_.size());
1071 link_type first = buckets_[n];
1072 size_type erased = 0;
1073
1074 if (first != nullptr) {
1075 link_type cur = first;
1076 link_type next = cur->next;
1077
1078 while (next != nullptr) {
1079 if (equals_(extracter_(next->data), key)) {
1080 cur->next = next->next;
1081 hashtable::delete_node(next);
1082 next = cur->next;
1083 ++erased;
1084 --size_;
1085 } else {
1086 cur = next;
1087 next = cur->next;
1088 }
1089 }
1090
1091 if (equals_(extracter_(first->data), key)) {
1092 buckets_[n] = first->next;
1093 hashtable::delete_node(first);
1094 ++erased;
1095 --size_;
1096 }
1097 }
1098
1099 return erased;
1100 }
1101
1108 if (position.current_ == nullptr || position.container_ != this) {
1109 return hashtable::end();
1110 }
1111
1112 const size_type n = position.bucket_;
1113 link_type const p = position.current_;
1114 link_type next_node = p->next;
1115
1116 link_type prev = nullptr;
1117 link_type curr = buckets_[n];
1118 while (curr != nullptr && curr != p) {
1119 prev = curr;
1120 curr = curr->next;
1121 }
1122
1123 if (curr == nullptr) {
1124 return hashtable::end();
1125 }
1126
1127 if (prev == nullptr) {
1128 buckets_[n] = next_node;
1129 } else {
1130 prev->next = next_node;
1131 }
1132
1133 hashtable::delete_node(p);
1134 --size_;
1135
1136 if (next_node != nullptr) {
1137 return iterator(next_node, n, this);
1138 }
1139
1140 for (size_type bucket = n + 1; bucket < buckets_.size(); ++bucket) {
1141 if (buckets_[bucket] != nullptr) {
1142 return iterator(buckets_[bucket], bucket, this);
1143 }
1144 }
1145 return hashtable::end();
1146 }
1147
1155 if (first == last) {
1156 return last;
1157 }
1158
1159 if (first.container_ != this || (last.container_ != this && last != end())) {
1160 return hashtable::end();
1161 }
1162
1163 size_type count_erased = 0;
1164
1165 if (first.bucket_ == last.bucket_) {
1166 count_erased = hashtable::erase_bucket_range(first.bucket_, first.current_, last.current_);
1167 } else {
1168 count_erased += hashtable::erase_bucket_range(first.bucket_, first.current_, nullptr);
1169 for (size_type bucket = first.bucket_ + 1; bucket < last.bucket_; ++bucket) {
1170 count_erased += hashtable::erase_bucket_completely(bucket);
1171 }
1172 if (last.bucket_ < buckets_.size()) {
1173 count_erased += hashtable::erase_bucket_range(last.bucket_, buckets_[last.bucket_], last.current_);
1174 }
1175 }
1176 size_ -= count_erased;
1177 return last;
1178 }
1179
1186 return hashtable::erase(iterator(position));
1187 }
1188
1198
1202 void clear() noexcept {
1203 for (size_type i = 0; i < buckets_.size(); ++i) {
1204 link_type cur = buckets_[i];
1205 while (cur != nullptr) {
1206 link_type next = cur->next;
1207 hashtable::delete_node(cur);
1208 cur = next;
1209 }
1210 buckets_[i] = nullptr;
1211 }
1212 size_ = 0;
1213 }
1214
1220 NEFORCE_NODISCARD iterator find(const key_type& key) noexcept(is_nothrow_hashable_v<key_type>) {
1221 if (buckets_.empty()) {
1222 return hashtable::end();
1223 }
1224
1225 size_type n = hashtable::bucket_index_key(key, buckets_.size());
1226 for (link_type first = buckets_[n]; first != nullptr; first = first->next) {
1227 if (equals_(extracter_(first->data), key)) {
1228 return iterator(first, n, this);
1229 }
1230 }
1231 return hashtable::end();
1232 }
1233
1239 NEFORCE_NODISCARD const_iterator find(const key_type& key) const noexcept(is_nothrow_hashable_v<key_type>) {
1240 if (buckets_.empty()) {
1241 return hashtable::cend();
1242 }
1243
1244 size_type n = hashtable::bucket_index_key(key, buckets_.size());
1245 for (link_type first = buckets_[n]; first != nullptr; first = first->next) {
1246 if (equals_(extracter_(first->data), key)) {
1247 return const_iterator(first, n, this);
1248 }
1249 }
1250 return hashtable::cend();
1251 }
1252
1258 NEFORCE_NODISCARD size_type count(const key_type& key) const noexcept(is_nothrow_hashable_v<key_type>) {
1259 if (buckets_.empty()) {
1260 return 0;
1261 }
1262 const size_type n = hashtable::bucket_index_key(key, buckets_.size());
1263 size_type result = 0;
1264 for (link_type cur = buckets_[n]; cur != nullptr; cur = cur->next) {
1265 if (equals_(extracter_(cur->data), key)) {
1266 ++result;
1267 }
1268 }
1269 return result;
1270 }
1271
1277 NEFORCE_NODISCARD bool contains(const key_type& key) const noexcept(is_nothrow_hashable_v<key_type>) {
1278 return hashtable::find(key) != cend();
1279 }
1280
1286 NEFORCE_NODISCARD pair<iterator, iterator> equal_range(const key_type& key) {
1287 if (buckets_.empty()) {
1288 return {hashtable::end(), hashtable::end()};
1289 }
1290
1291 const size_type n = hashtable::bucket_index_key(key, buckets_.size());
1292 link_type first_match = nullptr;
1293 link_type last_match = nullptr;
1294
1295 for (link_type curr = buckets_[n]; curr != nullptr; curr = curr->next) {
1296 if (equals_(extracter_(curr->data), key)) {
1297 if (first_match == nullptr) {
1298 first_match = curr;
1299 }
1300 last_match = curr;
1301 } else if (first_match != nullptr) {
1302 break;
1303 }
1304 }
1305
1306 if (first_match == nullptr) {
1307 return {hashtable::end(), hashtable::end()};
1308 }
1309
1310 link_type range_end = (last_match != nullptr) ? last_match->next : nullptr;
1311 return {iterator(first_match, n, this), iterator(range_end, n, this)};
1312 }
1313
1319 NEFORCE_NODISCARD pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
1320 if (buckets_.empty()) {
1321 return {cend(), cend()};
1322 }
1323
1324 const size_type n = hashtable::bucket_index_key(key, buckets_.size());
1325 const link_type first_match = nullptr;
1326 const link_type last_match = nullptr;
1327
1328 for (const link_type curr = buckets_[n]; curr != nullptr; curr = curr->next) {
1329 if (equals_(extracter_(curr->data), key)) {
1330 if (first_match == nullptr) {
1331 first_match = curr;
1332 }
1333 last_match = curr;
1334 } else if (first_match != nullptr) {
1335 break;
1336 }
1337 }
1338
1339 if (first_match == nullptr) {
1340 return {hashtable::cend(), hashtable::cend()};
1341 }
1342
1343 const link_type range_end = (last_match != nullptr) ? last_match->next : nullptr;
1344 return {const_iterator(first_match, n, this), const_iterator(range_end, n, this)};
1345 }
1346
1353 if (_NEFORCE addressof(other) == this) {
1354 return;
1355 }
1356 _NEFORCE swap(hasher_, other.hasher_);
1357 _NEFORCE swap(equals_, other.equals_);
1358 _NEFORCE swap(extracter_, other.extracter_);
1359 buckets_.swap(other.buckets_);
1360 _NEFORCE swap(size_, other.size_);
1361 pair_.swap(other.pair_);
1362 }
1363
1369 NEFORCE_NODISCARD bool operator==(const hashtable& rhs) const {
1370 if (size_ != rhs.size_) {
1371 return false;
1372 }
1373 if (size_ == 0) {
1374 return true;
1375 }
1376 if (this == &rhs) {
1377 return true;
1378 }
1379
1380 if (size_ < 100) {
1381 return hashtable::equal_small(rhs);
1382 }
1383 return hashtable::equal_large(rhs);
1384 }
1385
1391 NEFORCE_NODISCARD bool operator<(const hashtable& rhs) const
1392 noexcept(noexcept(_NEFORCE lexicographical_compare(hashtable::cbegin(), hashtable::cend(), rhs.cbegin(),
1393 rhs.cend()))) {
1394 return _NEFORCE lexicographical_compare(hashtable::cbegin(), hashtable::cend(), rhs.cbegin(), rhs.cend());
1395 }
1396};
1397 // HashTable
1399
1400NEFORCE_END_NAMESPACE__
1401#endif // NEFORCE_CORE_CONTAINER_HASHTABLE_HPP__
哈希表容器
NEFORCE_NODISCARD bool empty() const noexcept
检查是否为空
NEFORCE_NODISCARD const_iterator cend() const noexcept
获取常量结束迭代器
pair< iterator, bool > insert_unique(value_type &&value)
移动插入元素(唯一键版本)
iterator insert_equal(const value_type &value)
插入元素(允许重复键版本)
hashtable(const size_type n, const HashFcn &hf, const EqualKey &eql, float max_lf=1.0f)
构造函数,指定哈希函数和相等比较函数
hashtable(const size_type n, float max_lf=1.0f)
构造函数
NEFORCE_NODISCARD float load_factor() const noexcept
获取当前负载因子
NEFORCE_NODISCARD bool contains(const key_type &key) const noexcept(is_nothrow_hashable_v< key_type >)
检查是否包含指定键
const Value * const_pointer
常量指针类型
HashFcn hasher
哈希函数类型
NEFORCE_NODISCARD hasher hash_func() const noexcept(is_nothrow_copy_constructible_v< hasher >)
获取哈希函数对象
NEFORCE_NODISCARD size_type buckets_size() const noexcept
获取桶数量
NEFORCE_NODISCARD iterator find(const key_type &key) noexcept(is_nothrow_hashable_v< key_type >)
查找具有指定键的元素
iterator erase(iterator first, iterator last) noexcept(is_nothrow_hashable_v< key_type >)
删除指定范围内的元素
ptrdiff_t difference_type
差值类型
iterator emplace_equal(Args &&... args)
构造元素(允许重复键版本)
pair< iterator, bool > emplace_unique(Args &&... args)
构造元素(唯一键版本)
Value * pointer
指针类型
hashtable(hashtable &&other) noexcept(noexcept(hashtable::swap(other)))
移动构造函数
NEFORCE_NODISCARD float max_load_factor() const noexcept
获取最大负载因子
NEFORCE_NODISCARD iterator end() noexcept
获取结束迭代器
hashtable & operator=(const hashtable &other)
拷贝赋值运算符
static NEFORCE_NODISCARD size_type buckets_max_size() noexcept
获取最大桶数量
iterator erase(const iterator &position) noexcept(is_nothrow_hashable_v< key_type >)
删除指定位置的元素
const Value & const_reference
常量引用类型
NEFORCE_NODISCARD bool operator==(const hashtable &rhs) const
相等比较操作符
hashtable_iterator< true, hashtable > const_iterator
常量迭代器类型
void insert_unique(std::initializer_list< value_type > ilist)
初始化列表插入元素(唯一键版本)
NEFORCE_NODISCARD const_iterator end() const noexcept
获取常量结束迭代器
NEFORCE_NODISCARD const_iterator cbegin() const noexcept
获取常量起始迭代器
void clear() noexcept
清空哈希表
NEFORCE_NODISCARD size_type bucket_size(size_type index) const noexcept
获取指定桶的大小
Alloc allocator_type
分配器类型
hashtable(const hashtable &other)
拷贝构造函数
Value & reference
引用类型
hashtable_iterator< false, hashtable > iterator
迭代器类型
NEFORCE_NODISCARD const_iterator begin() const noexcept
获取常量起始迭代器
NEFORCE_NODISCARD size_type count(const key_type &key) const noexcept(is_nothrow_hashable_v< key_type >)
统计具有指定键的元素数量
NEFORCE_NODISCARD bool operator<(const hashtable &rhs) const noexcept(noexcept(_NEFORCE lexicographical_compare(hashtable::cbegin(), hashtable::cend(), rhs.cbegin(), rhs.cend())))
小于比较操作符
NEFORCE_NODISCARD key_equal key_eql() const noexcept(is_nothrow_copy_constructible_v< key_equal >)
获取键相等比较函数对象
Value value_type
值类型
size_t size_type
大小类型
enable_if_t< is_iter_v< Iterator > > insert_unique(Iterator first, Iterator last)
范围插入元素(唯一键版本)
size_type erase(const key_type &key) noexcept(is_nothrow_hashable_v< key_type >)
删除所有具有指定键的元素
hashtable & operator=(hashtable &&other) noexcept(noexcept(hashtable::swap(other)))
移动赋值运算符
void max_load_factor(const float lf) noexcept
设置最大负载因子
~hashtable()
析构函数
iterator insert_equal(value_type &&value)
移动插入元素(允许重复键版本)
hashtable(const size_type n, const HashFcn &hf, float max_lf=1.0f)
构造函数,指定哈希函数
void reserve(const size_type n)
预留空间
Key key_type
键类型
NEFORCE_NODISCARD const_iterator find(const key_type &key) const noexcept(is_nothrow_hashable_v< key_type >)
查找具有指定键的元素(常量版本)
const_iterator erase(const const_iterator &position) noexcept(is_nothrow_hashable_v< key_type >)
删除指定位置的元素(常量迭代器版本)
const_iterator erase(const_iterator first, const_iterator last) noexcept(is_nothrow_hashable_v< key_type >)
删除指定范围内的元素(常量迭代器版本)
NEFORCE_NODISCARD pair< iterator, iterator > equal_range(const key_type &key)
获取等于指定键的元素范围
EqualKey key_equal
键相等比较函数类型
pair< iterator, bool > insert_unique(const value_type &value)
插入元素(唯一键版本)
void swap(hashtable &other) noexcept(is_nothrow_swappable_v< HashFcn > &&is_nothrow_swappable_v< EqualKey > &&is_nothrow_swappable_v< allocator_type >)
交换两个哈希表的内容
hashtable(const size_type n, const HashFcn &hf, const EqualKey &eql, const ExtractKey &ext, float max_lf=1.0f)
构造函数,指定所有函数对象
NEFORCE_NODISCARD size_type max_size() const noexcept
获取最大可能大小
enable_if_t< is_iter_v< Iterator > > insert_equal(Iterator first, Iterator last)
范围插入元素(允许重复键版本)
NEFORCE_NODISCARD size_type bucket_index(const key_type &key) const noexcept(is_nothrow_hashable_v< key_type >)
获取键的桶索引
void insert_equal(std::initializer_list< value_type > ilist)
初始化列表插入元素(允许重复键版本)
NEFORCE_NODISCARD pair< const_iterator, const_iterator > equal_range(const key_type &key) const
获取等于指定键的元素范围(常量版本)
NEFORCE_NODISCARD size_type size() const noexcept
获取元素数量
void rehash(const size_type new_size)
重新哈希,调整桶数量
NEFORCE_NODISCARD iterator begin() noexcept
获取起始迭代器
动态大小数组容器
NEFORCE_CONSTEXPR20 void push_back(const T &value)
在末尾拷贝插入元素
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 iterator end() noexcept
获取结束迭代器
NEFORCE_CONSTEXPR20 void reserve(const size_type n)
预留容量
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 iterator begin() noexcept
获取起始迭代器
NEFORCE_NODISCARD constexpr T * addressof(T &x) noexcept
获取对象的地址
NEFORCE_NODISCARD constexpr T && forward(remove_reference_t< T > &x) noexcept
完美转发左值
NEFORCE_INLINE17 constexpr size_t extent_v
extent的便捷变量模板
constexpr Iterator lower_bound(Iterator first, Iterator last, const T &value, Compare comp)
查找有序范围中第一个不小于指定值的元素位置
constexpr const T & max(const T &a, const T &b, Compare comp) noexcept(noexcept(comp(a, b)))
返回两个值中的较大者
NEFORCE_NODISCARD constexpr bool lexicographical_compare(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2, Compare comp) noexcept(noexcept(++first1) &&noexcept(++first2) &&noexcept(comp(*first1, *first2)) &&noexcept(first1==last1 &&first2 !=last2))
字典序比较两个范围
constexpr iter_difference_t< Iterator > count_if(Iterator first, Iterator last, const T &value, BinaryPredicate pred)
统计范围内满足二元谓词的元素数量
#define NEFORCE_DEBUG_VERIFY(CON, MESG)
调试模式断言
NEFORCE_INLINE17 constexpr bool is_nothrow_hashable_v
is_nothrow_hashable的便捷变量模板
NEFORCE_INLINE17 constexpr size_t HASH_PRIMER_COUNT
素数列表长度
NEFORCE_BEGIN_CONSTANTS__ NEFORCE_INLINE17 constexpr size_t HASH_PRIME_LIST[]
哈希表素数列表(32位系统)
NEFORCE_CONSTEXPR20 T * construct(T *ptr, Args &&... args) noexcept(is_nothrow_constructible_v< T, Args... >)
在指定内存位置构造对象
NEFORCE_CONSTEXPR20 void destroy(T *pointer) noexcept(is_nothrow_destructible_v< T >)
销毁单个对象
constexpr Iterator prev(Iterator iter, iter_difference_t< Iterator > n=-1)
获取迭代器的前一个位置
constexpr Iterator next(Iterator iter, iter_difference_t< Iterator > n=1)
获取迭代器的后一个位置
constexpr iter_difference_t< Iterator > distance(Iterator first, Iterator last)
计算两个迭代器之间的距离
NEFORCE_CONST_FUNCTION NEFORCE_CONSTEXPR14 decimal_t ceil(const decimal_t x) noexcept
向上取整
uint64_t size_t
无符号大小类型
int64_t ptrdiff_t
指针差类型
constexpr Iterator2 move(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__move_aux(first, last, result)))
移动范围元素
constexpr Iterator2 copy(Iterator1 first, Iterator1 last, Iterator2 result) noexcept(noexcept(inner::__copy_aux(first, last, result)))
复制范围元素
void sort(Iterator first, Iterator last, Compare comp)
标准排序
void swap()=delete
删除无参数的swap重载
NEFORCE_INLINE17 constexpr bool is_nothrow_swappable_v
is_nothrow_swappable的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_nothrow_default_constructible_v
is_nothrow_default_constructible的便捷变量模板
NEFORCE_INLINE17 constexpr bool is_nothrow_copy_constructible_v
is_nothrow_copy_constructible的便捷变量模板
typename conditional< Test, T1, T2 >::type conditional_t
conditional的便捷别名
typename enable_if< Test, T >::type enable_if_t
enable_if的便捷别名
排序算法
默认构造标签
前向迭代器标签
NEFORCE_NODISCARD bool equal(const hashtable_iterator &rhs) const noexcept
相等比较
typename container_type::size_type size_type
大小类型
typename container_type::difference_type difference_type
差值类型
conditional_t< IsConst, typename container_type::const_pointer, typename container_type::pointer > pointer
指针类型
conditional_t< IsConst, typename container_type::const_reference, typename container_type::reference > reference
引用类型
forward_iterator_tag iterator_category
迭代器类别(前向迭代器)
NEFORCE_NODISCARD reference dereference() const noexcept
解引用操作
typename container_type::value_type value_type
值类型
NEFORCE_NODISCARD const container_type * container() const noexcept
获取关联容器
NEFORCE_NODISCARD pointer base() const noexcept
获取底层指针
void increment() noexcept
递增操作
HashTable container_type
容器类型
哈希表节点
hashtable_node() noexcept(is_nothrow_default_constructible_v< T >)
默认构造函数
集合器接口模板
迭代器接口模板
存储两个值的元组对
变量处理异常
动态大小数组容器