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.