1

How can the unordered_set can hold both (0, 1) and (1, 0) if they have the same hash value?

#include <iostream>
#include <unordered_set>
#include <utility>

using namespace std;

struct PairHash
{
    template <class T1, class T2>
    size_t operator()(pair<T1, T2> const &p) const
    {
        size_t hash_first = hash<T1>{}(p.first);
        size_t hash_second = hash<T2>{}(p.second);
        size_t hash_combined = hash_first ^ hash_second;
        
        cout << hash_first << ", " << hash_second << ", " << hash_combined << endl;
        
        return hash_combined;    
    }
};

int main()
{
    unordered_set<pair<int, int>, PairHash> map;
    map.insert({0, 1});
    map.insert({1, 0});
    
    cout << map.size() << endl;

    for (auto& entry : map) {
        cout << entry.first << ", " << entry.second << endl;
    }

    return 0;
}

Output:

0, 1, 1
1, 0, 1
2
1, 0
0, 1

Link to onlinegdb.

Rushikesh Talokar
  • 1,515
  • 4
  • 16
  • 32
andrey
  • 1,515
  • 5
  • 23
  • 39

1 Answers1

4

unordered_set can hold one instance of any unique data-value; it is not limited to only holding data-values with unique hash-values. In particular, when two data-values are different (according to their == operator) but both hash to the same hash-value, the unordered_set will make arrangements to hold both of them regardless, usually at a slightly reduced efficiency (since any hash-based lookups for either of them will internally hash to a data structure that holds both of them, which the unordered_set's lookup-code will have to iterate over until it finds the one it is looking for)

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 1
    Btw for an amusing party trick, try changing your `operator()` to just always return 0, and watch how that affects the `unordered_set`'s performance as you add more and more different values to the set :) – Jeremy Friesner Jun 01 '21 at 23:35