Learning outcome: Store and retrieve key-value pairs with Map, use HashMap correctly, traverse map views, and explain how key equality affects lookup.
A map associates each unique key with one value. It is useful when the program should find information by an identifier rather than by a numeric position: a roll number to a student name, a product code to a price, or a word to its frequency.
In simple terms: a map is the contacts app on a phone. Nobody remembers that a number sits at position 47; it is looked up by name. The name is the key, the number is the value, and one name holds only one entry.
Map Is a Key-Value Abstraction
Map<Integer, String> students = new HashMap<>();
students.put(101, "Aarav");
students.put(102, "Diya");
students.put(103, "Kabir");
Integer is the key type and String is the value type. A map cannot contain duplicate keys. Adding the same key again replaces its old value.
students.put(102, "Diya Sharma");
System.out.println(students.get(102));
Output:
Diya Sharma
Values do not need to be unique. Several keys may map to the same value.
In simple terms: keys must be unique, values need not be. Two roll numbers may both map to the name "Aarav", but one roll number cannot map to two names, because the second put overwrites the first.
Map vs Collection
Map<K,V> belongs to the Collections Framework but does not extend Collection<E>. A collection stores individual elements; a map stores associations.
| Structure | Stored unit | Uniqueness rule | Typical access |
|---|---|---|---|
List<E> |
Element | Duplicates allowed | Index |
Set<E> |
Element | Elements unique | Membership test |
Map<K,V> |
Key-value entry | Keys unique | Key lookup |
Core Map Operations
| Method | Purpose |
|---|---|
put(key, value) |
Adds or replaces a mapping |
putIfAbsent(key, value) |
Adds only when the key has no value mapping |
get(key) |
Returns the mapped value or null |
getOrDefault(key, defaultValue) |
Returns a fallback when the key is absent |
containsKey(key) |
Tests whether the key exists |
containsValue(value) |
Tests whether a value occurs |
remove(key) |
Removes a mapping by key |
replace(key, value) |
Replaces the value only when the key exists |
size() |
Returns the number of mappings |
clear() |
Removes all mappings |
When a map permits null values, get(key) == null cannot distinguish an absent key from a key mapped to null. Use containsKey when that distinction matters.
In simple terms: get returning null can mean two different things: "there is no such key" or "the key exists and its value is null". Only containsKey tells them apart.
Map Views
A map exposes three live views of its contents.
In simple terms: a view is a window on the map, not a photocopy. keySet() shows only the keys, values() only the values, and entrySet() the key-value pairs. Because they are windows, removing through a view also removes from the map.
| View method | Returned view |
|---|---|
keySet() |
A set of keys |
values() |
A collection of values |
entrySet() |
A set of Map.Entry<K,V> pairs |
For both key and value, iterate through entrySet().
import java.util.HashMap;
import java.util.Map;
public class StudentMap {
public static void main(String[] args) {
Map<Integer, String> students = new HashMap<>();
students.put(101, "Aarav");
students.put(102, "Diya");
students.put(103, "Kabir");
for (Map.Entry<Integer, String> entry : students.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}
Sample output:
101 -> Aarav
102 -> Diya
103 -> Kabir
The order is intentionally unspecified because the implementation is HashMap.
Why entrySet() and not keySet()? Looping over keySet() and calling get(key) inside makes the map search for the same entry a second time. entrySet() hands over the key and the value together in one step.
HashMap Characteristics
HashMap<K,V> stores entries in a hash-table structure.
| Characteristic | Meaning |
|---|---|
| Unique keys | A second put for an equal key replaces the value |
| Null support | One null key and multiple null values are permitted |
| Ordering | No iteration-order guarantee |
| Synchronisation | Not synchronized by default |
| Basic performance | get and put are expected constant time with well-distributed hashes |
Do not write logic that depends on the order printed by a HashMap. Use LinkedHashMap when insertion order is required or TreeMap when sorted key order is required.
How Hash Lookup Works
HashMap uses hashCode() to narrow the search to a bucket and equals() to identify the matching key. Therefore:
- Equal keys must have equal hash codes.
- Keys should not change in a way that affects equality while they are stored.
- Custom key classes should implement
equalsandhashCodeconsistently.
In simple terms: hashCode() is the shelf number in a library and equals() is reading the title to confirm the right book. Searching one shelf is fast; searching the whole library is not. That is why two keys that are equals must return the same hash code, otherwise the search looks on the wrong shelf and never finds the entry.
A mutable key can become impossible to locate if a field used by hashCode changes after insertion.
Counting Frequencies
Maps are ideal for counting occurrences. The merge method can insert the first count and update later counts with one expression.
Reading merge(word, 1, Integer::sum): "if word is new, store 1; if it is already present, combine the stored count with 1 using sum." One line replaces an if-else built from containsKey and get.
import java.util.HashMap;
import java.util.Map;
public class WordFrequency {
public static void main(String[] args) {
String[] words = {"java", "map", "java", "set", "map", "java"};
Map<String, Integer> frequency = new HashMap<>();
for (String word : words) {
frequency.merge(word, 1, Integer::sum);
}
System.out.println("java = " + frequency.get("java"));
System.out.println("set = " + frequency.getOrDefault("set", 0));
System.out.println("list = " + frequency.getOrDefault("list", 0));
}
}
Output:
java = 3
set = 1
list = 0
Grouping Values by Key
computeIfAbsent is useful when each key should own a collection.
Reading computeIfAbsent("Java", key -> new ArrayList<>()): "give me the list stored for Java; if there is none yet, create an empty list, store it, and give me that one." The returned list is then added to directly, so no separate null check is needed.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CourseGroups {
public static void main(String[] args) {
Map<String, List<String>> groups = new HashMap<>();
groups.computeIfAbsent("Java", key -> new ArrayList<>()).add("Aarav");
groups.computeIfAbsent("Java", key -> new ArrayList<>()).add("Diya");
groups.computeIfAbsent("Networks", key -> new ArrayList<>()).add("Kabir");
System.out.println(groups.get("Java"));
}
}
Output:
[Aarav, Diya]
Mutable and Unmodifiable Maps
Map.of creates a compact unmodifiable map.
Map<String, Integer> credits = Map.of(
"Java", 4,
"Networks", 3,
"Database", 4);
Use it for fixed data. Copy it into a HashMap when updates are needed.
Map<String, Integer> editable = new HashMap<>(credits);
editable.put("Project", 2);
Common Mistakes
| Mistake | Correction |
|---|---|
| Expecting duplicate keys | A later value replaces the earlier value for an equal key. |
Depending on printed HashMap order |
The order is not guaranteed. |
Looping over keySet() and calling get(key) inside |
Iterate entrySet() when both key and value are needed. |
| Using a mutable object as a key and changing equality fields | Prefer immutable keys. |
Assuming get(key) == null means absent |
Check containsKey when null values are possible. |
Modifying a Map.of result |
Copy it into a mutable implementation first. |
Practice
- Create a map from three roll numbers to student names and print every entry.
- Count the frequency of characters in a word.
- Use
getOrDefaultto return zero for an absent product quantity. - Group student names by section using
computeIfAbsent.
Quick Check
1. Can a map contain duplicate keys?
No. Each key maps to at most one value. A later mapping for an equal key replaces the earlier value.
2. Which view is best when both keys and values are needed?
Use entrySet() and iterate over Map.Entry<K,V> objects.
3. Does HashMap preserve insertion order?
No. HashMap makes no guarantee about iteration order.
4. Why must custom keys implement equals and hashCode consistently?
HashMap uses both methods to locate and identify keys. Equal keys must produce equal hash codes.
Lecture Quiz
Which method updates a word count by inserting 1 for a new word and adding 1 for an existing word?
frequency.merge(word, 1, Integer::sum) performs both cases.
Summary
Map models associations between unique keys and values. HashMap provides efficient hash-based lookup, permits nulls, and does not guarantee order. Map views support traversal, while methods such as merge and computeIfAbsent make frequency counting and grouping concise.