Loading learning content...
You've mastered the concepts of hash sets and hash maps. You understand their internal mechanics, know when to use each, and can reason about their performance characteristics. But there's one more challenge: every programming language has its own names for these structures.
Python calls it a dict. Java calls it HashMap. JavaScript has both Object and Map. C# uses Dictionary. Go has map. Rust has HashMap. The same fundamental data structure wears different costumes in different languages.
This linguistic diversity can trip up developers moving between languages, reading documentation, or participating in technical discussions. This page serves as your Rosetta Stone—a comprehensive guide to how different languages name, implement, and expose set and map functionality.
By the end of this page, you will understand: the naming conventions across major programming languages, the historical and philosophical reasons behind naming choices, API differences and gotchas when switching languages, implementation variations (hash vs ordered), and how to quickly translate your knowledge to any new language.
Understanding why languages chose different names illuminates the concepts and helps us remember the variations.
Languages like Python and C# use Dictionary (or dict) because the data structure resembles a language dictionary:
This metaphor emphasizes the lookup aspect—you have a term and want its associated information.
Languages like Java, JavaScript, and Go use Map because the data structure represents a mathematical mapping:
This metaphor emphasizes the relationship aspect—a formal correspondence between domains.
Some languages (especially older ones like Perl, AWK, and PHP) use Associative Array because:
This metaphor emphasizes the generalized indexing aspect.
Languages like Java, Rust, and C++ use prefixes like HashMap or HashSet to distinguish hash-based implementations from:
TreeMap / TreeSet (balanced BST-based, ordered)LinkedHashMap / LinkedHashSet (hash + linked list, insertion-ordered)This naming explicitly indicates the implementation strategy.
| Philosophy | Languages | Rationale |
|---|---|---|
| Dictionary metaphor | Python, C#, Swift | Intuitive for beginners, real-world analogy |
| Map (mathematical) | Java, Go, C++ | Formal, emphasizes function-like mapping |
| Implementation-explicit | Java, Rust, C++ | Distinguishes hash vs tree vs linked |
| Primitive syntax | JavaScript (Object) | Literal syntax built into language |
| Associative Array | PHP, Perl, AWK | Historical, arrays with non-integer keys |
None of these naming choices is 'wrong.' They reflect different ways of thinking about the same underlying concept. A 'dictionary' emphasizes lookup, a 'map' emphasizes the mathematical relationship, and 'hash map' emphasizes the implementation. All refer to the key-value associative data structure.
Python's hash-based structures are deeply integrated into the language, with literal syntax support and extensive standard library utilities.
dictPython's dict is the workhorse key-value store:
# Literal syntax
ages = {"Alice": 30, "Bob": 25, "Carol": 35}
# Constructor syntaxes
ages = dict(Alice=30, Bob=25, Carol=35)
ages = dict([("Alice", 30), ("Bob", 25)])
Key characteristics:
{key: value, ...}{k: v for k, v in items}set and frozensetPython distinguishes between mutable and immutable sets:
# Mutable set
colors = {"red", "green", "blue"}
colors.add("yellow")
# Immutable set (hashable, can be dict key or set element)
immutable = frozenset(["a", "b", "c"])
Key characteristics:
{element1, element2, ...}{} creates an empty dict, not set! Use set() for empty set.|, &, -, ^ operatorsfrozenset is hashable (can be in sets or as dict keys)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
# ═══════════════════════════════════════════════════════════════════# PYTHON DICT - Complete API Reference# ═══════════════════════════════════════════════════════════════════ user = {"name": "Alice", "age": 30} # Basic operationsuser["email"] = "alice@example.com" # Insert/updatename = user["name"] # Access (raises KeyError if missing)name = user.get("name") # Safe access (returns None if missing)name = user.get("title", "Unknown") # Safe access with default del user["email"] # Delete (raises KeyError if missing)email = user.pop("email", None) # Delete and return (safe with default) # Membershipexists = "name" in user # Truenot_exists = "phone" not in user # True # Iterationfor key in user: # Iterate over keys print(key)for key in user.keys(): # Explicit keys view print(key)for value in user.values(): # Iterate over values print(value)for key, value in user.items(): # Iterate over pairs print(f"{key}: {value}") # Modificationuser.update({"phone": "555-1234", "age": 31}) # Bulk updateuser |= {"status": "active"} # Update operator (Python 3.9+)merged = user | {"role": "admin"} # Merge (new dict, Python 3.9+) # Special operationsuser.setdefault("country", "USA") # Set only if missingcleared = user.copy() # Shallow copyuser.clear() # Remove all items # ═══════════════════════════════════════════════════════════════════# PYTHON SET - Complete API Reference# ═══════════════════════════════════════════════════════════════════ colors = {"red", "green", "blue"}more_colors = {"blue", "yellow", "purple"} # Basic operationscolors.add("orange") # Add elementcolors.remove("red") # Remove (raises KeyError if missing)colors.discard("pink") # Remove (no error if missing)color = colors.pop() # Remove and return arbitrary element # Membershipis_present = "green" in colors # O(1) lookup # Set algebra - operatorsunion = colors | more_colors # Unionintersection = colors & more_colors # Intersectiondifference = colors - more_colors # Differencesymmetric = colors ^ more_colors # Symmetric difference # Set algebra - methods (more flexible, accept any iterable)union = colors.union(more_colors)intersection = colors.intersection(more_colors)difference = colors.difference(more_colors) # In-place modificationscolors.update(more_colors) # |= unioncolors.intersection_update(more_colors) # &= intersectioncolors.difference_update(more_colors) # -= difference # Subset/supersetis_subset = colors <= more_colors # colors.issubset(more_colors)is_superset = colors >= more_colors # colors.issuperset(more_colors)is_proper_subset = colors < more_colorsare_disjoint = colors.isdisjoint(more_colors) # No common elements # ═══════════════════════════════════════════════════════════════════# SPECIALIZED DICT VARIANTS (collections module)# ═══════════════════════════════════════════════════════════════════ from collections import defaultdict, OrderedDict, Counter # defaultdict - auto-initialize missing keysgroups = defaultdict(list)groups["fruits"].append("apple") # No KeyError! # Counter - specialized for countingcounts = Counter(["a", "b", "a", "c", "a", "b"])print(counts) # Counter({'a': 3, 'b': 2, 'c': 1})print(counts.most_common(2)) # [('a', 3), ('b', 2)] # OrderedDict - insertion order (redundant since 3.7, but has move_to_end)od = OrderedDict()od["first"] = 1od["second"] = 2od.move_to_end("first") # Move to end (for LRU caches)JavaScript has a complex history with key-value structures, offering both the traditional Object and the ES6 Map and Set classes.
Historically, JavaScript Object served as the primary key-value store:
// Object as dictionary
const user = { name: "Alice", age: 30 };
console.log(user["name"]); // "Alice"
console.log(user.name); // "Alice" (property access syntax)
Limitations of Object as a map:
Object.prototype (key collisions with methods like toString)size propertyES6 introduced Map as a proper map implementation:
const map = new Map();
map.set("name", "Alice");
map.get("name"); // "Alice"
map.has("name"); // true
Advantages over Object:
.size propertyES6 also introduced Set:
const set = new Set([1, 2, 3, 2, 1]);
console.log(set.size); // 3 (duplicates removed)
set.has(2); // true
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
// ═══════════════════════════════════════════════════════════════════// JAVASCRIPT MAP - Complete API Reference// ═══════════════════════════════════════════════════════════════════ const map = new Map(); // Basic operationsmap.set("key1", "value1"); // Set (returns the map for chaining)map.set("key2", "value2").set("key3", "value3"); // Chaining const value = map.get("key1"); // Get (undefined if missing)const exists = map.has("key1"); // Check existence map.delete("key1"); // Delete (returns true if existed)const size = map.size; // Number of entries (property, not method!) // Any type as key!const objKey = { id: 1 };map.set(objKey, "object as key");map.get(objKey); // Works!map.get({ id: 1 }); // undefined - different object! // Iterationfor (const [key, value] of map) { console.log(`${key}: ${value}`);} map.forEach((value, key) => { console.log(`${key}: ${value}`);}); const keys = [...map.keys()]; // Array of keysconst values = [...map.values()]; // Array of valuesconst entries = [...map.entries()]; // Array of [key, value] // Initialize from entriesconst map2 = new Map([ ["a", 1], ["b", 2], ["c", 3]]); // Convert between Map and Objectconst obj = Object.fromEntries(map2); // Map → Object (string keys only)const backToMap = new Map(Object.entries(obj)); // Object → Map map.clear(); // Remove all entries // ═══════════════════════════════════════════════════════════════════// JAVASCRIPT SET - Complete API Reference// ═══════════════════════════════════════════════════════════════════ const set = new Set(); // Basic operationsset.add(1); // Add (returns set for chaining)set.add(2).add(3).add(2); // Chaining, duplicates ignoredconst hasTwo = set.has(2); // trueset.delete(2); // Remove (returns true if existed)const size2 = set.size; // Number of elements // Initialize from arrayconst set2 = new Set([1, 2, 3, 2, 1]); // {1, 2, 3} // Deduplicate arrayconst unique = [...new Set([1, 2, 2, 3, 1])]; // [1, 2, 3] // Iterationfor (const value of set) { console.log(value);} set.forEach(value => console.log(value)); // Note: Set has keys(), values(), entries() for compatibility// keys() === values() for Set// entries() returns [value, value] pairs // Set algebra (not built-in, must implement)const a = new Set([1, 2, 3]);const b = new Set([2, 3, 4]); const union = new Set([...a, ...b]); // {1, 2, 3, 4}const intersection = new Set([...a].filter(x => b.has(x))); // {2, 3}const difference = new Set([...a].filter(x => !b.has(x))); // {1} // Note: ES2025 adds native set algebra methods!// a.union(b), a.intersection(b), a.difference(b), etc. // ═══════════════════════════════════════════════════════════════════// WHEN TO USE OBJECT vs MAP// ═══════════════════════════════════════════════════════════════════ // Use Object when:// - Keys are known string literals (config objects)// - JSON serialization needed (Map doesn't serialize cleanly)// - Destructuring is desired: const { name, age } = user;// - Pattern matching or spread: { ...obj, newProp: value } // Use Map when:// - Keys are not strings (objects, symbols, mixed types)// - Frequent additions/deletions// - Need .size property// - Need guaranteed iteration order// - Avoid prototype pollution concernsJavaScript beginners often use Object for everything. If your keys come from user input or are dynamic, use Map to avoid prototype pollution issues. "__proto__" and "constructor" as Object keys can cause security vulnerabilities!
Java's Collections Framework provides a rich hierarchy of map and set implementations, each optimized for different use cases.
Map<K,V> (interface)
├── HashMap<K,V> → Unordered, O(1) operations
├── LinkedHashMap<K,V> → Insertion ordered (or access ordered)
├── TreeMap<K,V> → Sorted by keys, O(log n) operations
├── Hashtable<K,V> → Legacy, synchronized (avoid)
├── ConcurrentHashMap<K,V> → Thread-safe, high concurrency
└── WeakHashMap<K,V> → Keys are weak references (GC-able)
Set<E> (interface)
├── HashSet<E> → Unordered, O(1) operations
├── LinkedHashSet<E> → Insertion ordered
├── TreeSet<E> → Sorted, O(log n) operations
├── EnumSet<E> → Optimized for enum elements
└── CopyOnWriteArraySet<E> → Thread-safe with copy semantics
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
import java.util.*; public class JavaHashStructures { public static void main(String[] args) { // ═══════════════════════════════════════════════════════════════ // HASHMAP - Complete API Reference // ═══════════════════════════════════════════════════════════════ Map<String, Integer> map = new HashMap<>(); // Basic operations map.put("Alice", 30); // Insert/update, returns old value int age = map.get("Alice"); // Get (null if missing) int ageOrDefault = map.getOrDefault("Bob", 0); // Safe get boolean hasAlice = map.containsKey("Alice"); // Key check boolean has30 = map.containsValue(30); // Value check (O(n)!) map.remove("Alice"); // Remove, returns removed value map.remove("Alice", 30); // Remove only if value matches // Advanced operations (Java 8+) map.putIfAbsent("Bob", 25); // Only set if key absent map.computeIfAbsent("Carol", k -> k.length()); // Compute if absent map.computeIfPresent("Carol", (k, v) -> v + 1); // Compute if present map.merge("David", 1, Integer::sum); // Merge with existing value // Iteration for (String key : map.keySet()) { System.out.println(key); } for (int value : map.values()) { System.out.println(value); } for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // forEach with lambda map.forEach((k, v) -> System.out.println(k + " -> " + v)); // ═══════════════════════════════════════════════════════════════ // HASHSET - Complete API Reference // ═══════════════════════════════════════════════════════════════ Set<String> set = new HashSet<>(); // Basic operations set.add("apple"); // Add (returns boolean) boolean wasNew = set.add("banana"); // true if newly added boolean exists = set.contains("apple"); boolean removed = set.remove("apple"); int size = set.size(); boolean empty = set.isEmpty(); // Bulk operations set.addAll(Arrays.asList("cherry", "date")); // Add all set.removeAll(Arrays.asList("cherry")); // Remove all set.retainAll(someOtherSet); // Intersection // Iteration for (String item : set) { System.out.println(item); } set.forEach(System.out::println); // Stream operations long count = set.stream() .filter(s -> s.startsWith("a")) .count(); // ═══════════════════════════════════════════════════════════════ // LINKEDHASHMAP - Preserves insertion order // ═══════════════════════════════════════════════════════════════ Map<String, Integer> ordered = new LinkedHashMap<>(); ordered.put("first", 1); ordered.put("second", 2); ordered.put("third", 3); // Iteration is in insertion order: first, second, third // Access-order mode (LRU cache) Map<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) { return size() > 100; // Keep max 100 entries } }; // ═══════════════════════════════════════════════════════════════ // TREEMAP/TREESET - Sorted order // ═══════════════════════════════════════════════════════════════ NavigableMap<String, Integer> sortedMap = new TreeMap<>(); sortedMap.put("banana", 2); sortedMap.put("apple", 1); sortedMap.put("cherry", 3); // Iteration is in sorted key order: apple, banana, cherry // NavigableMap extras String first = sortedMap.firstKey(); // "apple" String last = sortedMap.lastKey(); // "cherry" String floor = sortedMap.floorKey("blueberry"); // "banana" (<=) String ceiling = sortedMap.ceilingKey("blueberry"); // "cherry" (>=) // Range views SortedMap<String, Integer> subMap = sortedMap.subMap("a", "c"); }}Let's survey how other major languages handle sets and maps.
C# follows the "dictionary" naming convention:
var dict = new Dictionary<string, int> {
["Alice"] = 30,
["Bob"] = 25
};
int age = dict["Alice"]; // Throws if missing
bool found = dict.TryGetValue("Carol", out int carolAge); // Safe
var set = new HashSet<string> { "a", "b", "c" };
set.Add("d");
bool has = set.Contains("a");
// Set operations
set.UnionWith(otherSet);
set.IntersectWith(otherSet);
set.ExceptWith(otherSet); // Difference
set.SymmetricExceptWith(otherSet);
Go has a built-in map type but no built-in set:
// Map with literal syntax
ages := map[string]int{
"Alice": 30,
"Bob": 25,
}
age := ages["Alice"] // Returns zero value if missing
age, ok := ages["Carol"] // Comma-ok idiom
if !ok {
// Key was missing
}
delete(ages, "Alice") // Remove
// Set: Use map[T]bool or map[T]struct{}
set := make(map[string]struct{})
set["apple"] = struct{}{}
if _, ok := set["apple"]; ok {
// Element exists
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445
// ═══════════════════════════════════════════════════════════════════// RUST — HashMap and HashSet// ═══════════════════════════════════════════════════════════════════ use std::collections::{HashMap, HashSet}; fn main() { // HashMap let mut map: HashMap<String, i32> = HashMap::new(); map.insert(String::from("Alice"), 30); // Returns Option<old_value> // Get returns Option<&V> if let Some(age) = map.get("Alice") { println!("Alice is {}", age); } // Entry API for conditional insert/update map.entry(String::from("Bob")).or_insert(25); map.entry(String::from("Alice")).and_modify(|v| *v += 1); // Iterate for (key, value) in &map { println!("{}: {}", key, value); } // HashSet let mut set: HashSet<String> = HashSet::new(); set.insert(String::from("apple")); // Returns bool (was it new?) let exists = set.contains("apple"); let removed = set.remove("apple"); // Set operations return iterators let a: HashSet<i32> = [1, 2, 3].iter().cloned().collect(); let b: HashSet<i32> = [2, 3, 4].iter().cloned().collect(); let union: HashSet<_> = a.union(&b).cloned().collect(); let intersection: HashSet<_> = a.intersection(&b).cloned().collect(); let difference: HashSet<_> = a.difference(&b).cloned().collect(); // Alternative: BTreeMap/BTreeSet for sorted keys use std::collections::BTreeMap; let sorted_map: BTreeMap<String, i32> = BTreeMap::new();}| Language | Map Type | Set Type | Notes |
|---|---|---|---|
| Swift | Dictionary<Key, Value> | Set<Element> | Value types, CoW optimization |
| Kotlin | HashMap<K, V> / MutableMap | HashSet<E> / MutableSet | Mutable vs read-only views |
| Ruby | Hash | Set (require 'set') | Hash is built-in, Set is stdlib |
| PHP | array (associative) | (no built-in set) | Arrays serve as both lists and maps |
| Scala | HashMap / Map | HashSet / Set | Immutable by default, mutable variants |
Here's your comprehensive reference table for translating between languages:
| Operation | Python | JavaScript (Map) | Java | C# | Go |
|---|---|---|---|---|---|
| Create | dict() or {} | new Map() | new HashMap<>() | new Dictionary<>() | make(map[K]V) |
| Insert/Update | d[k] = v | m.set(k, v) | m.put(k, v) | d[k] = v | m[k] = v |
| Get | d[k] or d.get(k) | m.get(k) | m.get(k) | d[k] or d.TryGet | m[k] or v, ok := m[k] |
| Delete | del d[k] | m.delete(k) | m.remove(k) | d.Remove(k) | delete(m, k) |
| Contains Key | k in d | m.has(k) | m.containsKey(k) | d.ContainsKey(k) | _, ok := m[k] |
| Size | len(d) | m.size | m.size() | d.Count | len(m) |
| Iterate Keys | for k in d | for(k of m.keys()) | for(k : m.keySet()) | foreach(k in d.Keys) | for k := range m |
| Iterate Pairs | for k,v in d.items() | for([k,v] of m) | for(e : m.entrySet()) | foreach((k,v) in d) | for k,v := range m |
| Operation | Python | JavaScript | Java | C# | Rust |
|---|---|---|---|---|---|
| Create | set() or {1,2} | new Set() | new HashSet<>() | new HashSet<>() | HashSet::new() |
| Add | s.add(x) | s.add(x) | s.add(x) | s.Add(x) | s.insert(x) |
| Remove | s.remove(x) | s.delete(x) | s.remove(x) | s.Remove(x) | s.remove(&x) |
| Contains | x in s | s.has(x) | s.contains(x) | s.Contains(x) | s.contains(&x) |
| Size | len(s) | s.size | s.size() | s.Count | s.len() |
| Union | a | b | (manual) | (manual) | a.UnionWith(b) | a.union(&b) |
| Intersection | a & b | (manual) | (manual) | a.IntersectWith(b) | a.intersection(&b) |
| Difference | a - b | (manual) | (manual) | a.ExceptWith(b) | a.difference(&b) |
Despite naming differences, the core patterns are universal: insert/put for adding, get for retrieval, contains/has for membership, remove/delete for removal. Learn the concepts once, then just look up the syntax for each language.
Most languages offer both hash-based (unordered) and tree-based (ordered) implementations. Understanding when to use each is crucial.
| Requirement | Use This Variant | Examples |
|---|---|---|
| Fastest operations, order irrelevant | Hash-based | HashMap, HashSet, dict, Map |
| Need sorted keys | Tree-based | TreeMap, TreeSet, BTreeMap, SortedDict |
| Need iteration in insertion order | Linked hash | LinkedHashMap, OrderedDict |
| Need range queries (keys between X and Y) | Tree-based | TreeMap.subMap(), BTreeMap.range() |
| Need min/max key efficiently | Tree-based | TreeMap.firstKey(), TreeSet.first() |
| LRU cache (access-ordered) | Linked hash | LinkedHashMap with access order |
123456789101112131415161718192021222324
# Python flavor: dict is insertion-ordered since 3.7# For sorted, use sortedcontainers or manual sorting from collections import OrderedDictfrom sortedcontainers import SortedDict, SortedSet # pip install sortedcontainers # Regular dict - insertion ordered, O(1)regular = {"b": 2, "a": 1, "c": 3}print(list(regular.keys())) # ['b', 'a', 'c'] - insertion order # SortedDict - key sorted, O(log n)sorted_d = SortedDict({"b": 2, "a": 1, "c": 3})print(list(sorted_d.keys())) # ['a', 'b', 'c'] - sorted order # Range queryprint(sorted_d.irange("a", "b")) # Keys from 'a' to 'b' (inclusive) # SortedSet - element sorted, O(log n)sorted_s = SortedSet([3, 1, 4, 1, 5, 9, 2, 6])print(list(sorted_s)) # [1, 2, 3, 4, 5, 6, 9] - sorted, unique # Bisect operationsprint(sorted_s.bisect_left(4)) # Index where 4 would go: 3We have navigated the landscape of set and map implementations across programming languages. Let's consolidate the key insights:
| Concept | Python | JavaScript | Java | Go |
|---|---|---|---|---|
| Hash Map | dict | Map (not Object) | HashMap | map |
| Hash Set | set | Set | HashSet | map[T]struct{} |
| Ordered Map | dict (3.7+) | Map (insertion) | LinkedHashMap | N/A (use slice) |
| Sorted Map | sortedcontainers | N/A | TreeMap | N/A |
| Counter | Counter | Manual | Manual | Manual |
Congratulations! You have completed the Hash Sets vs Hash Maps module. You now understand both data structures at a deep level, know when to use each, and can translate your knowledge across programming languages. This knowledge forms a critical foundation for efficient data manipulation in any software engineering context.