flat_hash_set.h (22079B)
1 // Copyright 2018 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // ----------------------------------------------------------------------------- 16 // File: flat_hash_set.h 17 // ----------------------------------------------------------------------------- 18 // 19 // An `absl::flat_hash_set<T>` is an unordered associative container designed to 20 // be a more efficient replacement for `std::unordered_set`. Like 21 // `unordered_set`, search, insertion, and deletion of set elements can be done 22 // as an `O(1)` operation. However, `flat_hash_set` (and other unordered 23 // associative containers known as the collection of Abseil "Swiss tables") 24 // contain other optimizations that result in both memory and computation 25 // advantages. 26 // 27 // In most cases, your default choice for a hash set should be a set of type 28 // `flat_hash_set`. 29 // 30 // `flat_hash_set` is not exception-safe. 31 32 #ifndef ABSL_CONTAINER_FLAT_HASH_SET_H_ 33 #define ABSL_CONTAINER_FLAT_HASH_SET_H_ 34 35 #include <cstddef> 36 #include <memory> 37 #include <type_traits> 38 #include <utility> 39 40 #include "absl/algorithm/container.h" 41 #include "absl/base/attributes.h" 42 #include "absl/base/macros.h" 43 #include "absl/container/hash_container_defaults.h" 44 #include "absl/container/internal/container_memory.h" 45 #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export 46 #include "absl/memory/memory.h" 47 #include "absl/meta/type_traits.h" 48 49 namespace absl { 50 ABSL_NAMESPACE_BEGIN 51 namespace container_internal { 52 template <typename T> 53 struct FlatHashSetPolicy; 54 } // namespace container_internal 55 56 // ----------------------------------------------------------------------------- 57 // absl::flat_hash_set 58 // ----------------------------------------------------------------------------- 59 // 60 // An `absl::flat_hash_set<T>` is an unordered associative container which has 61 // been optimized for both speed and memory footprint in most common use cases. 62 // Its interface is similar to that of `std::unordered_set<T>` with the 63 // following notable differences: 64 // 65 // * Requires keys that are MoveConstructible 66 // * Supports heterogeneous lookup, through `find()` and `insert()`, provided 67 // that the set is provided a compatible heterogeneous hashing function and 68 // equality operator. See below for details. 69 // * Invalidates any references and pointers to elements within the table after 70 // `rehash()` and when the table is moved. 71 // * Contains a `capacity()` member function indicating the number of element 72 // slots (open, deleted, and empty) within the hash set. 73 // * Returns `void` from the `erase(iterator)` overload. 74 // 75 // By default, `flat_hash_set` uses the `absl::Hash` hashing framework. All 76 // fundamental and Abseil types that support the `absl::Hash` framework have a 77 // compatible equality operator for comparing insertions into `flat_hash_set`. 78 // If your type is not yet supported by the `absl::Hash` framework, see 79 // absl/hash/hash.h for information on extending Abseil hashing to user-defined 80 // types. 81 // 82 // Using `absl::flat_hash_set` at interface boundaries in dynamically loaded 83 // libraries (e.g. .dll, .so) is unsupported due to way `absl::Hash` values may 84 // be randomized across dynamically loaded libraries. 85 // 86 // To achieve heterogeneous lookup for custom types either `Hash` and `Eq` type 87 // parameters can be used or `T` should have public inner types 88 // `absl_container_hash` and (optionally) `absl_container_eq`. In either case, 89 // `typename Hash::is_transparent` and `typename Eq::is_transparent` should be 90 // well-formed. Both types are basically functors: 91 // * `Hash` should support `size_t operator()(U val) const` that returns a hash 92 // for the given `val`. 93 // * `Eq` should support `bool operator()(U lhs, V rhs) const` that returns true 94 // if `lhs` is equal to `rhs`. 95 // 96 // In most cases `T` needs only to provide the `absl_container_hash`. In this 97 // case `std::equal_to<void>` will be used instead of `eq` part. 98 // 99 // NOTE: A `flat_hash_set` stores its keys directly inside its implementation 100 // array to avoid memory indirection. Because a `flat_hash_set` is designed to 101 // move data when rehashed, set keys will not retain pointer stability. If you 102 // require pointer stability, consider using 103 // `absl::flat_hash_set<std::unique_ptr<T>>`. If your type is not moveable and 104 // you require pointer stability, consider `absl::node_hash_set` instead. 105 // 106 // PERFORMANCE WARNING: Erasure & sparsity can negatively affect performance: 107 // * Iteration takes O(capacity) time, not O(size). 108 // * erase() slows down begin() and ++iterator. 109 // * Capacity only shrinks on rehash() or clear() -- not on erase(). 110 // 111 // Example: 112 // 113 // // Create a flat hash set of three strings 114 // absl::flat_hash_set<std::string> ducks = 115 // {"huey", "dewey", "louie"}; 116 // 117 // // Insert a new element into the flat hash set 118 // ducks.insert("donald"); 119 // 120 // // Force a rehash of the flat hash set 121 // ducks.rehash(0); 122 // 123 // // See if "dewey" is present 124 // if (ducks.contains("dewey")) { 125 // std::cout << "We found dewey!" << std::endl; 126 // } 127 template <class T, class Hash = DefaultHashContainerHash<T>, 128 class Eq = DefaultHashContainerEq<T>, 129 class Allocator = std::allocator<T>> 130 class ABSL_ATTRIBUTE_OWNER flat_hash_set 131 : public absl::container_internal::raw_hash_set< 132 absl::container_internal::FlatHashSetPolicy<T>, Hash, Eq, Allocator> { 133 using Base = typename flat_hash_set::raw_hash_set; 134 135 public: 136 // Constructors and Assignment Operators 137 // 138 // A flat_hash_set supports the same overload set as `std::unordered_set` 139 // for construction and assignment: 140 // 141 // * Default constructor 142 // 143 // // No allocation for the table's elements is made. 144 // absl::flat_hash_set<std::string> set1; 145 // 146 // * Initializer List constructor 147 // 148 // absl::flat_hash_set<std::string> set2 = 149 // {{"huey"}, {"dewey"}, {"louie"},}; 150 // 151 // * Copy constructor 152 // 153 // absl::flat_hash_set<std::string> set3(set2); 154 // 155 // * Copy assignment operator 156 // 157 // // Hash functor and Comparator are copied as well 158 // absl::flat_hash_set<std::string> set4; 159 // set4 = set3; 160 // 161 // * Move constructor 162 // 163 // // Move is guaranteed efficient 164 // absl::flat_hash_set<std::string> set5(std::move(set4)); 165 // 166 // * Move assignment operator 167 // 168 // // May be efficient if allocators are compatible 169 // absl::flat_hash_set<std::string> set6; 170 // set6 = std::move(set5); 171 // 172 // * Range constructor 173 // 174 // std::vector<std::string> v = {"a", "b"}; 175 // absl::flat_hash_set<std::string> set7(v.begin(), v.end()); 176 flat_hash_set() {} 177 using Base::Base; 178 179 // flat_hash_set::begin() 180 // 181 // Returns an iterator to the beginning of the `flat_hash_set`. 182 using Base::begin; 183 184 // flat_hash_set::cbegin() 185 // 186 // Returns a const iterator to the beginning of the `flat_hash_set`. 187 using Base::cbegin; 188 189 // flat_hash_set::cend() 190 // 191 // Returns a const iterator to the end of the `flat_hash_set`. 192 using Base::cend; 193 194 // flat_hash_set::end() 195 // 196 // Returns an iterator to the end of the `flat_hash_set`. 197 using Base::end; 198 199 // flat_hash_set::capacity() 200 // 201 // Returns the number of element slots (assigned, deleted, and empty) 202 // available within the `flat_hash_set`. 203 // 204 // NOTE: this member function is particular to `absl::flat_hash_set` and is 205 // not provided in the `std::unordered_set` API. 206 using Base::capacity; 207 208 // flat_hash_set::empty() 209 // 210 // Returns whether or not the `flat_hash_set` is empty. 211 using Base::empty; 212 213 // flat_hash_set::max_size() 214 // 215 // Returns the largest theoretical possible number of elements within a 216 // `flat_hash_set` under current memory constraints. This value can be thought 217 // of the largest value of `std::distance(begin(), end())` for a 218 // `flat_hash_set<T>`. 219 using Base::max_size; 220 221 // flat_hash_set::size() 222 // 223 // Returns the number of elements currently within the `flat_hash_set`. 224 using Base::size; 225 226 // flat_hash_set::clear() 227 // 228 // Removes all elements from the `flat_hash_set`. Invalidates any references, 229 // pointers, or iterators referring to contained elements. 230 // 231 // NOTE: this operation may shrink the underlying buffer. To avoid shrinking 232 // the underlying buffer call `erase(begin(), end())`. 233 using Base::clear; 234 235 // flat_hash_set::erase() 236 // 237 // Erases elements within the `flat_hash_set`. Erasing does not trigger a 238 // rehash. Overloads are listed below. 239 // 240 // void erase(const_iterator pos): 241 // 242 // Erases the element at `position` of the `flat_hash_set`, returning 243 // `void`. 244 // 245 // NOTE: returning `void` in this case is different than that of STL 246 // containers in general and `std::unordered_set` in particular (which 247 // return an iterator to the element following the erased element). If that 248 // iterator is needed, simply post increment the iterator: 249 // 250 // set.erase(it++); 251 // 252 // iterator erase(const_iterator first, const_iterator last): 253 // 254 // Erases the elements in the open interval [`first`, `last`), returning an 255 // iterator pointing to `last`. The special case of calling 256 // `erase(begin(), end())` resets the reserved growth such that if 257 // `reserve(N)` has previously been called and there has been no intervening 258 // call to `clear()`, then after calling `erase(begin(), end())`, it is safe 259 // to assume that inserting N elements will not cause a rehash. 260 // 261 // size_type erase(const key_type& key): 262 // 263 // Erases the element with the matching key, if it exists, returning the 264 // number of elements erased (0 or 1). 265 using Base::erase; 266 267 // flat_hash_set::insert() 268 // 269 // Inserts an element of the specified value into the `flat_hash_set`, 270 // returning an iterator pointing to the newly inserted element, provided that 271 // an element with the given key does not already exist. If rehashing occurs 272 // due to the insertion, all iterators are invalidated. Overloads are listed 273 // below. 274 // 275 // std::pair<iterator,bool> insert(const T& value): 276 // 277 // Inserts a value into the `flat_hash_set`. Returns a pair consisting of an 278 // iterator to the inserted element (or to the element that prevented the 279 // insertion) and a bool denoting whether the insertion took place. 280 // 281 // std::pair<iterator,bool> insert(T&& value): 282 // 283 // Inserts a moveable value into the `flat_hash_set`. Returns a pair 284 // consisting of an iterator to the inserted element (or to the element that 285 // prevented the insertion) and a bool denoting whether the insertion took 286 // place. 287 // 288 // iterator insert(const_iterator hint, const T& value): 289 // iterator insert(const_iterator hint, T&& value): 290 // 291 // Inserts a value, using the position of `hint` as a non-binding suggestion 292 // for where to begin the insertion search. Returns an iterator to the 293 // inserted element, or to the existing element that prevented the 294 // insertion. 295 // 296 // void insert(InputIterator first, InputIterator last): 297 // 298 // Inserts a range of values [`first`, `last`). 299 // 300 // NOTE: Although the STL does not specify which element may be inserted if 301 // multiple keys compare equivalently, for `flat_hash_set` we guarantee the 302 // first match is inserted. 303 // 304 // void insert(std::initializer_list<T> ilist): 305 // 306 // Inserts the elements within the initializer list `ilist`. 307 // 308 // NOTE: Although the STL does not specify which element may be inserted if 309 // multiple keys compare equivalently within the initializer list, for 310 // `flat_hash_set` we guarantee the first match is inserted. 311 using Base::insert; 312 313 // flat_hash_set::emplace() 314 // 315 // Inserts an element of the specified value by constructing it in-place 316 // within the `flat_hash_set`, provided that no element with the given key 317 // already exists. 318 // 319 // The element may be constructed even if there already is an element with the 320 // key in the container, in which case the newly constructed element will be 321 // destroyed immediately. 322 // 323 // If rehashing occurs due to the insertion, all iterators are invalidated. 324 using Base::emplace; 325 326 // flat_hash_set::emplace_hint() 327 // 328 // Inserts an element of the specified value by constructing it in-place 329 // within the `flat_hash_set`, using the position of `hint` as a non-binding 330 // suggestion for where to begin the insertion search, and only inserts 331 // provided that no element with the given key already exists. 332 // 333 // The element may be constructed even if there already is an element with the 334 // key in the container, in which case the newly constructed element will be 335 // destroyed immediately. 336 // 337 // If rehashing occurs due to the insertion, all iterators are invalidated. 338 using Base::emplace_hint; 339 340 // flat_hash_set::extract() 341 // 342 // Extracts the indicated element, erasing it in the process, and returns it 343 // as a C++17-compatible node handle. Overloads are listed below. 344 // 345 // node_type extract(const_iterator position): 346 // 347 // Extracts the element at the indicated position and returns a node handle 348 // owning that extracted data. 349 // 350 // node_type extract(const key_type& x): 351 // 352 // Extracts the element with the key matching the passed key value and 353 // returns a node handle owning that extracted data. If the `flat_hash_set` 354 // does not contain an element with a matching key, this function returns an 355 // empty node handle. 356 using Base::extract; 357 358 // flat_hash_set::merge() 359 // 360 // Extracts elements from a given `source` flat hash set into this 361 // `flat_hash_set`. If the destination `flat_hash_set` already contains an 362 // element with an equivalent key, that element is not extracted. 363 using Base::merge; 364 365 // flat_hash_set::swap(flat_hash_set& other) 366 // 367 // Exchanges the contents of this `flat_hash_set` with those of the `other` 368 // flat hash set. 369 // 370 // All iterators and references on the `flat_hash_set` remain valid, excepting 371 // for the past-the-end iterator, which is invalidated. 372 // 373 // `swap()` requires that the flat hash set's hashing and key equivalence 374 // functions be Swappable, and are exchanged using unqualified calls to 375 // non-member `swap()`. If the set's allocator has 376 // `std::allocator_traits<allocator_type>::propagate_on_container_swap::value` 377 // set to `true`, the allocators are also exchanged using an unqualified call 378 // to non-member `swap()`; otherwise, the allocators are not swapped. 379 using Base::swap; 380 381 // flat_hash_set::rehash(count) 382 // 383 // Rehashes the `flat_hash_set`, setting the number of slots to be at least 384 // the passed value. If the new number of slots increases the load factor more 385 // than the current maximum load factor 386 // (`count` < `size()` / `max_load_factor()`), then the new number of slots 387 // will be at least `size()` / `max_load_factor()`. 388 // 389 // To force a rehash, pass rehash(0). 390 // 391 // NOTE: unlike behavior in `std::unordered_set`, references are also 392 // invalidated upon a `rehash()`. 393 using Base::rehash; 394 395 // flat_hash_set::reserve(count) 396 // 397 // Sets the number of slots in the `flat_hash_set` to the number needed to 398 // accommodate at least `count` total elements without exceeding the current 399 // maximum load factor, and may rehash the container if needed. 400 using Base::reserve; 401 402 // flat_hash_set::contains() 403 // 404 // Determines whether an element comparing equal to the given `key` exists 405 // within the `flat_hash_set`, returning `true` if so or `false` otherwise. 406 using Base::contains; 407 408 // flat_hash_set::count(const Key& key) const 409 // 410 // Returns the number of elements comparing equal to the given `key` within 411 // the `flat_hash_set`. note that this function will return either `1` or `0` 412 // since duplicate elements are not allowed within a `flat_hash_set`. 413 using Base::count; 414 415 // flat_hash_set::equal_range() 416 // 417 // Returns a closed range [first, last], defined by a `std::pair` of two 418 // iterators, containing all elements with the passed key in the 419 // `flat_hash_set`. 420 using Base::equal_range; 421 422 // flat_hash_set::find() 423 // 424 // Finds an element with the passed `key` within the `flat_hash_set`. 425 using Base::find; 426 427 // flat_hash_set::bucket_count() 428 // 429 // Returns the number of "buckets" within the `flat_hash_set`. Note that 430 // because a flat hash set contains all elements within its internal storage, 431 // this value simply equals the current capacity of the `flat_hash_set`. 432 using Base::bucket_count; 433 434 // flat_hash_set::load_factor() 435 // 436 // Returns the current load factor of the `flat_hash_set` (the average number 437 // of slots occupied with a value within the hash set). 438 using Base::load_factor; 439 440 // flat_hash_set::max_load_factor() 441 // 442 // Manages the maximum load factor of the `flat_hash_set`. Overloads are 443 // listed below. 444 // 445 // float flat_hash_set::max_load_factor() 446 // 447 // Returns the current maximum load factor of the `flat_hash_set`. 448 // 449 // void flat_hash_set::max_load_factor(float ml) 450 // 451 // Sets the maximum load factor of the `flat_hash_set` to the passed value. 452 // 453 // NOTE: This overload is provided only for API compatibility with the STL; 454 // `flat_hash_set` will ignore any set load factor and manage its rehashing 455 // internally as an implementation detail. 456 using Base::max_load_factor; 457 458 // flat_hash_set::get_allocator() 459 // 460 // Returns the allocator function associated with this `flat_hash_set`. 461 using Base::get_allocator; 462 463 // flat_hash_set::hash_function() 464 // 465 // Returns the hashing function used to hash the keys within this 466 // `flat_hash_set`. 467 using Base::hash_function; 468 469 // flat_hash_set::key_eq() 470 // 471 // Returns the function used for comparing keys equality. 472 using Base::key_eq; 473 }; 474 475 // erase_if(flat_hash_set<>, Pred) 476 // 477 // Erases all elements that satisfy the predicate `pred` from the container `c`. 478 // Returns the number of erased elements. 479 template <typename T, typename H, typename E, typename A, typename Predicate> 480 typename flat_hash_set<T, H, E, A>::size_type erase_if( 481 flat_hash_set<T, H, E, A>& c, Predicate pred) { 482 return container_internal::EraseIf(pred, &c); 483 } 484 485 // swap(flat_hash_set<>, flat_hash_set<>) 486 // 487 // Swaps the contents of two `flat_hash_set` containers. 488 // 489 // NOTE: we need to define this function template in order for 490 // `flat_hash_set::swap` to be called instead of `std::swap`. Even though we 491 // have `swap(raw_hash_set&, raw_hash_set&)` defined, that function requires a 492 // derived-to-base conversion, whereas `std::swap` is a function template so 493 // `std::swap` will be preferred by compiler. 494 template <typename T, typename H, typename E, typename A> 495 void swap(flat_hash_set<T, H, E, A>& x, 496 flat_hash_set<T, H, E, A>& y) noexcept(noexcept(x.swap(y))) { 497 return x.swap(y); 498 } 499 500 namespace container_internal { 501 502 // c_for_each_fast(flat_hash_set<>, Function) 503 // 504 // Container-based version of the <algorithm> `std::for_each()` function to 505 // apply a function to a container's elements. 506 // There is no guarantees on the order of the function calls. 507 // Erasure and/or insertion of elements in the function is not allowed. 508 template <typename T, typename H, typename E, typename A, typename Function> 509 decay_t<Function> c_for_each_fast(const flat_hash_set<T, H, E, A>& c, 510 Function&& f) { 511 container_internal::ForEach(f, &c); 512 return f; 513 } 514 template <typename T, typename H, typename E, typename A, typename Function> 515 decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>& c, Function&& f) { 516 container_internal::ForEach(f, &c); 517 return f; 518 } 519 template <typename T, typename H, typename E, typename A, typename Function> 520 decay_t<Function> c_for_each_fast(flat_hash_set<T, H, E, A>&& c, Function&& f) { 521 container_internal::ForEach(f, &c); 522 return f; 523 } 524 525 } // namespace container_internal 526 527 namespace container_internal { 528 529 template <class T> 530 struct FlatHashSetPolicy { 531 using slot_type = T; 532 using key_type = T; 533 using init_type = T; 534 using constant_iterators = std::true_type; 535 536 template <class Allocator, class... Args> 537 static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { 538 absl::allocator_traits<Allocator>::construct(*alloc, slot, 539 std::forward<Args>(args)...); 540 } 541 542 // Return std::true_type in case destroy is trivial. 543 template <class Allocator> 544 static auto destroy(Allocator* alloc, slot_type* slot) { 545 absl::allocator_traits<Allocator>::destroy(*alloc, slot); 546 return IsDestructionTrivial<Allocator, slot_type>(); 547 } 548 549 static T& element(slot_type* slot) { return *slot; } 550 551 template <class F, class... Args> 552 static decltype(absl::container_internal::DecomposeValue( 553 std::declval<F>(), std::declval<Args>()...)) 554 apply(F&& f, Args&&... args) { 555 return absl::container_internal::DecomposeValue( 556 std::forward<F>(f), std::forward<Args>(args)...); 557 } 558 559 static size_t space_used(const T*) { return 0; } 560 561 template <class Hash> 562 static constexpr HashSlotFn get_hash_slot_fn() { 563 return &TypeErasedApplyToSlotFn<Hash, T>; 564 } 565 }; 566 } // namespace container_internal 567 568 namespace container_algorithm_internal { 569 570 // Specialization of trait in absl/algorithm/container.h 571 template <class Key, class Hash, class KeyEqual, class Allocator> 572 struct IsUnorderedContainer<absl::flat_hash_set<Key, Hash, KeyEqual, Allocator>> 573 : std::true_type {}; 574 575 } // namespace container_algorithm_internal 576 577 ABSL_NAMESPACE_END 578 } // namespace absl 579 580 #endif // ABSL_CONTAINER_FLAT_HASH_SET_H_