Hash sets and hash maps (hash tables) are built on exactly the same underlying mechanism — hashing a key to an index for average O(1) operations — but they answer two different questions. A hash set answers "is this value present?" A hash map answers "what value is associated with this key?"
Hash Set: Keys Only, No Duplicates
A hash set stores a collection of unique values with no associated data — just the keys themselves. Internally, it's really a hash map where every value is ignored (or set to a dummy placeholder); the data structure is solely about which keys exist.
seen = set()
seen.add("apple")
seen.add("banana")
seen.add("apple") # duplicate — silently ignored
print(seen)
print("apple" in seen) # O(1) average membership check
print(len(seen))
Output:
{'apple', 'banana'}
True
2
Hash Map: Key-Value Pairs
A hash map stores a key alongside an associated value, letting you retrieve that value instantly by its key:
prices = {}
prices["apple"] = 0.50
prices["banana"] = 0.30
print(prices["apple"]) # retrieve the value for a key
print("apple" in prices) # checks keys, same as a set would
Output:
0.5
True
When to Use Which
Use a hash set when you only care about membership or uniqueness — deduplicating a list, checking if an item has already been visited in a graph traversal, or finding the intersection/union of two collections. Use a hash map whenever each key needs an associated piece of data attached — counting word frequencies (word → count), caching function results (arguments → return value), or looking up a user record by ID (ID → user object).
A quick rule of thumb: if you find yourself reaching for a set and then immediately wanting to also store "and what's its count/value," that's a sign you actually want a map instead. Both structures inherit the same strengths and weaknesses from hashing — fast average-case lookups, but performance that depends on good collision handling and a healthy load factor.
