Map Interface
java map interface HashMap LinkedHashMap TreeMap Hashtable hashCode equals load factor rehashing ConcurrentHashMap
Introduction
In the previous lesson, you learned about the Set Interface, which stores unique elements.
However, many real-world applications require storing data as key-value pairs.
For example:
| Key | Value |
|---|---|
| Employee ID | Employee Name |
| Product ID | Product Details |
| Username | Password |
| Country Code | Country Name |
| Student Roll Number | Student Record |
A List stores only values.
A Set stores only unique values.
Neither is suitable for storing associations between two pieces of information.
Java solves this problem using the Map Interface.
The Map interface stores data as key-value pairs, enabling extremely fast lookups.
Map is one of the most widely used data structures in:
- Spring Boot
- Hibernate
- REST APIs
- Banking Applications
- Caching Systems
- Authentication Systems
- Microservices
- Distributed Systems
Mastering the Map interface is essential for Java interviews.
In this lesson, you'll learn:
- What is Map?
- Why Map is Needed
- Map Hierarchy
- HashMap
- LinkedHashMap
- TreeMap
- Hashtable
- Internal Working of HashMap
- Hashing
- Buckets
- Load Factor
- Resizing (Rehashing)
- Collision Handling
- Java 8 Treeification
- equals() & hashCode()
- Performance Analysis
- Best Practices
- Common Mistakes
- Hands-on Exercises
- Professional Interview Questions
What is a Map?
A Map stores data in the form of key-value pairs.
Each key is unique.
Each key maps to exactly one value.
Example:
Map<Integer, String> employees =new HashMap<>();employees.put(101, "Rahul");employees.put(102, "Amit");employees.put(103, "Jagannath");
Retrieving:
System.out.println(employees.get(102));
Output:
Amit
Why Map?
Suppose we need to find an employee by ID.
Using List:
List<Employee> employees;
Searching requires iterating through the list.
Using Map:
Map<Integer, Employee> employees;
Searching becomes much faster.
Benefits:
- Fast lookup
- Unique keys
- Easy updates
- Efficient retrieval
- Excellent scalability
Map Hierarchy
Unlike List and Set, Map does not extend Collection.
Map│┌────────┼──────────┐│ │ │HashMap LinkedHashMap TreeMap│Hashtable
Common Map Methods
put()get()remove()containsKey()containsValue()keySet()values()entrySet()size()clear()isEmpty()
Example:
Map<Integer,String> map =new HashMap<>();map.put(1,"Java");System.out.println(map.containsKey(1));
Output:
true
HashMap
HashMap is the most commonly used implementation of the Map interface.
Characteristics:
- Fast lookups
- Unique keys
- Allows one null key
- Allows multiple null values
- No ordering guarantee
Example:
Map<String,String> capitals =new HashMap<>();capitals.put("India","New Delhi");capitals.put("Japan","Tokyo");
Internal Working of HashMap
Internally:
HashMap↓Array of Buckets↓Bucket↓Linked List↓(Red-Black Tree if heavily collided)
Each bucket stores entries based on the key's hash value.
Hashing Process
When inserting:
map.put("Java",100);
Steps:
- Compute
hashCode() - Calculate bucket index
- Check for existing key using
equals() - Replace value if key exists
- Otherwise create a new entry
Flow:
Key↓hashCode()↓Bucket↓equals()↓Store
Buckets
Example:
Bucket 0↓(Spring,100)Bucket 1↓(Java,200)Bucket 2↓(Hibernate,300)
Multiple entries may exist in the same bucket if collisions occur.
Hash Collisions
A collision occurs when two different keys map to the same bucket.
Example:
Key A↓Bucket 5Key B↓Bucket 5
Java resolves collisions using linked lists and, from Java 8 onward, converts long collision chains into balanced trees under specific conditions.
Java 8 Treeification
Before Java 8:
Bucket↓Node↓Node↓Node↓Node
After Java 8 (when thresholds are met):
Bucket↓Red-Black Tree
This improves worst-case lookup performance from approximately O(n) to O(log n) for heavily collided buckets.
Load Factor
The Load Factor determines when a HashMap should resize.
Default:
0.75
Formula:
Current Entries──────────────Capacity
When the load factor threshold is exceeded, the HashMap grows and redistributes its entries.
Rehashing (Resizing)
Suppose capacity:
16
When the threshold is exceeded:
16↓32
The existing entries are redistributed across the new bucket array.
Although resizing is expensive, it occurs infrequently, allowing HashMap to maintain excellent average performance.
HashMap Time Complexity
| Operation | Average | Worst Case* |
|---|---|---|
| put() | O(1) | O(log n) |
| get() | O(1) | O(log n) |
| remove() | O(1) | O(log n) |
*Worst-case assumes Java 8+ treeified buckets where applicable.
LinkedHashMap
LinkedHashMap extends HashMap by maintaining insertion order.
Example:
Map<Integer,String> students =new LinkedHashMap<>();students.put(1,"Rahul");students.put(2,"Amit");students.put(3,"John");
Output:
1=Rahul2=Amit3=John
Internally, it combines hashing with a doubly linked list.
TreeMap
TreeMap stores entries sorted by their keys.
Internally, it uses a Red-Black Tree.
Example:
Map<Integer,String> marks =new TreeMap<>();marks.put(80,"A");marks.put(95,"A+");marks.put(70,"B");
Output:
70=B80=A95=A+
Characteristics:
- Sorted keys
- No null keys
- O(log n) operations
Hashtable
Hashtable is a legacy synchronized implementation.
Example:
Hashtable<Integer,String> table =new Hashtable<>();table.put(1,"Java");
Characteristics:
- Thread-safe
- No null keys
- No null values
- Slower due to synchronization
Modern applications typically use ConcurrentHashMap for concurrent access instead of Hashtable.
HashMap vs LinkedHashMap vs TreeMap vs Hashtable
| Feature | HashMap | LinkedHashMap | TreeMap | Hashtable |
|---|---|---|---|---|
| Ordering | None | Insertion | Sorted Keys | None |
| Null Key | One | One | No | No |
| Null Values | Yes | Yes | Yes | No |
| Thread-Safe | No | No | No | Yes |
| Average Search | O(1) | O(1) | O(log n) | O(1) |
equals() and hashCode()
For custom key classes:
class Employee {int id;String name;}
Always override:
equals()hashCode()
Without them:
- Duplicate logical keys may be stored incorrectly.
- Retrieval may fail because the map cannot locate the intended bucket.
Iterating Through a Map
Using entrySet():
for(Map.Entry<Integer,String> entry: employees.entrySet()){System.out.println(entry.getKey() + " : " +entry.getValue());}
Using keySet():
for(Integer id : employees.keySet()){System.out.println(id);}
Using values():
for(String name : employees.values()){System.out.println(name);}
Real-World Example: Employee Directory
Map<Integer,String> employees =new HashMap<>();employees.put(101,"Rahul");employees.put(102,"Amit");employees.put(103,"Jagannath");
Finding an employee:
System.out.println(employees.get(103));
Real-World Example: Shopping Cart
Map<String,Integer> cart =new HashMap<>();cart.put("Laptop",2);cart.put("Mouse",5);cart.put("Keyboard",3);
Best Practices
Use HashMap by Default
Choose HashMap unless ordering or sorting is required.
Override equals() and hashCode()
Always override both methods when using custom key objects.
Prefer entrySet() for Iteration
It avoids unnecessary lookups and is generally more efficient than iterating over keySet() followed by get().
Use Immutable Keys
Changing a key's state after insertion can make it impossible to retrieve.
Use ConcurrentHashMap for Concurrent Access
Avoid using Hashtable in new applications unless maintaining legacy code.
Common Mistakes
Using Mutable Keys
Changing key fields after insertion breaks map lookups.
Forgetting equals() and hashCode()
Custom key retrieval may fail or produce duplicate logical entries.
Assuming HashMap Preserves Order
It does not guarantee insertion or sorted order.
Using TreeMap Without Comparable or Comparator
Keys must be naturally comparable or a Comparator must be supplied.
Iterating Using keySet() Unnecessarily
When both key and value are needed, prefer entrySet().
Hands-on Exercise
Create a Java program that:
- Creates a
HashMapof employee IDs and names. - Demonstrates insertion, retrieval, update, and deletion.
- Iterates using:
- entrySet()
- keySet()
- values()
- Creates a
LinkedHashMapand verifies insertion order. - Creates a
TreeMapand verifies sorted keys. - Creates a custom
EmployeeKeyclass with properequals()andhashCode(). - Demonstrates the effect of duplicate keys.
- Compares the behavior of
HashMap,LinkedHashMap,TreeMap, andHashtable.
Summary
The Map interface provides an efficient mechanism for storing and retrieving key-value pairs. HashMap offers excellent average-case performance, LinkedHashMap preserves insertion order, TreeMap maintains sorted keys using a Red-Black Tree, and Hashtable provides legacy synchronized access. Understanding hashing, bucket distribution, load factors, collision handling, and proper implementation of equals() and hashCode() is essential for writing high-performance enterprise Java applications.
Key Takeaways
Mapstores key-value pairs.- Keys are unique; values may be duplicated.
Mapis not part of theCollectioninterface hierarchy.HashMapprovides fast average-case lookup using hashing.LinkedHashMappreserves insertion order.TreeMapmaintains sorted keys.Hashtableis synchronized but largely replaced byConcurrentHashMap.- Proper
equals()andhashCode()implementations are essential for custom keys. entrySet()is generally the preferred way to iterate through a map.
Professional Interview Questions
1What is the difference between HashMap, LinkedHashMap, TreeMap, and Hashtable?
Professional Answer
HashMap provides fast average-case access without guaranteeing order. LinkedHashMap extends HashMap by maintaining insertion order. TreeMap stores entries sorted by key using a Red-Black Tree, providing O(log n) operations. Hashtable is a synchronized legacy implementation that does not allow null keys or values and has largely been superseded by ConcurrentHashMap for concurrent programming.
Follow-up Questions
- Which implementation preserves insertion order?
- Which implementation stores keys in sorted order?
- Which implementation is thread-safe?
Interview Tip: Remember: HashMap → Fast, LinkedHashMap → Ordered, TreeMap → Sorted, Hashtable → Legacy Synchronized.
2Why must equals() and hashCode() be overridden for custom keys?
Professional Answer
HashMap uses hashCode() to determine the bucket in which a key should be stored and equals() to determine whether two keys are logically equal. If these methods are not implemented consistently, retrieval may fail or logically identical keys may be treated as different objects.
Follow-up Questions
- What happens if two equal objects return different hash codes?
- Can a poor hashCode() implementation affect performance?
Interview Tip: The contract is: If a.equals(b) is true, then a.hashCode() must equal b.hashCode().
3What is the purpose of the Load Factor in HashMap?
Professional Answer
The load factor determines how full a HashMap is allowed to become before it resizes its internal bucket array. The default load factor is 0.75, which provides a practical balance between memory usage and lookup performance. When the threshold (capacity × loadFactor) is exceeded, the map resizes and redistributes its entries.
Follow-up Questions
- What is rehashing?
- Why is resizing expensive?
- Can the load factor be customized?
Interview Tip: A common interview fact: Default Capacity = 16, Default Load Factor = 0.75, Threshold = Capacity × Load Factor.