Therefore operator[] and at() return copies, since they do not return an iterator. The returned value is const, to remind you that changes do not affect the value in the map.
erase() calls the hash function, and may fail if the hash function throws an exception.
The interface adds assign_if_equal, since find() doesn't take a lock.
Only const version of find() is supported, and const iterators. Mutation must use functions provided, like assign().
iteration iterates over all the buckets in the table, unlike std::unordered_map which iterates over a linked list of elements. If the table is sparse, this may be more expensive. 1: ConcurrentHashMap, based on Java's ConcurrentHashMap. Very similar to std::unordered_map in performance.
2: ConcurrentHashMapSIMD, based on F14ValueMap. If the map is larger than the cache size, it has superior performance due to vectorized key lookup.
USAGE FAQs
Q: Is simultaneous iteration and erase() threadsafe? Example: ConcurrentHashMap<int, int> map; Thread 1: auto it = map.begin(); while (it != map.end()) { // Do something with it it++; } Thread 2: map.insert(2, 2); map.erase(2); A: Yes, this is safe. However, the iterating thread is not guaranteed to see (or not see) concurrent insertions and erasures. Inserts may cause a rehash, but the old table is still valid as long as any iterator pointing to it exists.
Q: How do I update an existing object atomically?
A: assign_if_equal is the recommended way - readers will see the old value until the new value is completely constructed and inserted.
Q: Are pointers to values safe to access withoutholding an iterator?
A: The SIMD version guarantees that references to elements are stable across rehashes, the non-SIMD version does not. Note that unless you hold an iterator, you need to ensure there are no concurrent deletes/updates to that key if you are accessing it via reference.
Q: Why do map.erase() and clear() not actually destroy elements?
A: Hazard Pointers are used to improve the performance of concurrent access. They can be thought of as a simple Garbage Collector. To reduce the GC overhead, a GC passis only run after reaching a certain memory bound. erase() will remove the element from being accessed via the map, but actual destruction may happen later, after iterators that may point to it have been deleted.
The only guarantee is that a GC pass will be run on map destruction - no elements will remain after map destruction.