{"id":21325635,"url":"https://github.com/andreysolovyev381/hash_table_no_invalidation","last_synced_at":"2025-08-01T19:39:42.566Z","repository":{"id":246836120,"uuid":"806094725","full_name":"andreysolovyev381/hash_table_no_invalidation","owner":"andreysolovyev381","description":"C++20 hash table that never invalidates its pointers and iterators. Slightly better than std::unordered - see ./benchmark","archived":false,"fork":false,"pushed_at":"2024-07-27T10:36:26.000Z","size":4644,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-22T12:29:51.960Z","etag":null,"topics":["cpp","cpp20","data-structures","hash-table","hashmap","hashtable","header-only","invalidating","invalidation"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andreysolovyev381.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-26T11:10:01.000Z","updated_at":"2024-07-27T10:36:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"928e608f-ffd7-4710-b7a7-e3c758f70fcd","html_url":"https://github.com/andreysolovyev381/hash_table_no_invalidation","commit_stats":null,"previous_names":["andreysolovyev381/hash_table_no_invalidation"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreysolovyev381%2Fhash_table_no_invalidation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreysolovyev381%2Fhash_table_no_invalidation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreysolovyev381%2Fhash_table_no_invalidation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreysolovyev381%2Fhash_table_no_invalidation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreysolovyev381","download_url":"https://codeload.github.com/andreysolovyev381/hash_table_no_invalidation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243806040,"owners_count":20350775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp20","data-structures","hash-table","hashmap","hashtable","header-only","invalidating","invalidation"],"created_at":"2024-11-21T21:06:48.553Z","updated_at":"2025-03-15T23:43:31.996Z","avatar_url":"https://github.com/andreysolovyev381.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Hash table with *NO* invalidation of pointers and iterators\n\n### Rational\nAnother project of mine required a **hash table that never invalidates** its pointers and iterators. I made one with a humble hope to be on par with `std::unordered`. Header only, single C++20 file, see ./include folder. See ./tests for more detailed usage examples. In general, more or less it reproduces `std::unordered` interface.\n\n### Short usage example\n```cpp\n#include \"include/hash_table.hpp\"\n...\n\n    ::containers::hash_table::Set\u003cint\u003e hashSet;\n    hashSet.insert(42);\n    auto found = hashSet.find(42);\n    if (found != hashSet.end()) {...}\n\n    ::containers::hash_table::Map\u003cint, int\u003e hashMap;\n    hashMap[1] = 42;\n    hashMap.insert(std::pair{42, 42});\n    for (auto const\u0026 [k, v] : hashMap) {...}\n\n```\n\n### Reference to other hash tables\nThere are many other hash tables, but I found that all of them are focused on performance, sacrificing exactly what I need — persistence of pointers and iterators. However, there are some serious attempts. Consider reading the [results](https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/) of benchmark made by people behind `robin_hood::hash_table`. I also recommend this [reading](https://greg7mdp.github.io/parallel-hashmap/) from Greg Popovitch.\n\n### Benchmark\nSee ./benchmark folder for code. \nHere are the results for 1 million ints. \n```\n------------------------------------------------------------------------------------------------\nBenchmark                                                      Time             CPU   Iterations\n------------------------------------------------------------------------------------------------\nInsertion std::unordered_set          /min_time:5.000      0.144 us        0.143 us     50408238\nInsertion containers::hash_table::Set /min_time:5.000      0.129 us        0.128 us     53205182\nAccess std::unordered_set             /min_time:5.000      0.072 us        0.072 us     96885052\nAccess containers::hash_table::Set    /min_time:5.000      0.067 us        0.066 us    105449693\nErase std::unordered_set              /min_time:5.000      0.009 us        0.009 us    618141538\nErase containers::hash_table::Set     /min_time:5.000      0.089 us        0.089 us     69120837\n\n```\n\n### Implementation details\n* Open addressing and linear probing as a collision resolution, in case anybody cares. \n\n* It is allowed to throw, you are the one who should catch. \n\n* Indeed, to nail down all the data, hash table should use a linked list as an underlying structure. The problem is that random memory placement turns out to be bad for cache locality. \nBut thanks to [Bloomberg](https://github.com/bloomberg) and their contribution to committee work, we have `std:pmr` namespace and polymorphic allocators.\nLong story short, this hash table is built upon `std::pmr::list` that allows to place list nodes in memory in an array-like fashion, thus making such a container more cache-friendly.\n\n  Another issue that comes from usage of `std::pmr` is a strong requirement to follow \"rule of five\". Specifically, see ./test/pmr.cpp — copy ctor and assignment fail if implemented by default. That is a reason why HashTable is a move-only. Implementation of allocator aware linked list may be a good solution here, ie see the root source of everything, [Pablo Halpern](https://github.com/phalpern)'s CppCon2017 [report](https://www.youtube.com/watch?v=v3dz-AKOVL8). But it is beyond my needs, so feel free to contribute :smirk:\n\n* My hypothesis is that after some insert / remove cycles this hash table will deteriorate in performance — \"pogrom is a pogrom\", list is a list, appearance of \"holes\" in that initial array-like placement is inevitable.\n\n### License\nMIT\n\n### Disclosure\nDespite heavy testing performed (see ./tests/test_11.txt), no guarantees of any kind are given whatsoever. Use it at your own risk.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreysolovyev381%2Fhash_table_no_invalidation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreysolovyev381%2Fhash_table_no_invalidation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreysolovyev381%2Fhash_table_no_invalidation/lists"}