Java Tutorial 0/145 lessons ~6 min read Lesson 27

    Map Interface

    java map interface HashMap LinkedHashMap TreeMap Hashtable hashCode equals load factor rehashing ConcurrentHashMap

    Course progress0%
    Focus
    28 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    KeyValue
    Employee IDEmployee Name
    Product IDProduct Details
    UsernamePassword
    Country CodeCountry Name
    Student Roll NumberStudent 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:

    code
    Map<Integer, String> employees =
    new HashMap<>();
    employees.put(101, "Rahul");
    employees.put(102, "Amit");
    employees.put(103, "Jagannath");

    Retrieving:

    code
    System.out.println(employees.get(102));

    Output:

    code
    Amit

    Why Map?

    Suppose we need to find an employee by ID.

    Using List:

    code
    List<Employee> employees;

    Searching requires iterating through the list.

    Using Map:

    code
    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.

    code
    Map
    ┌────────┼──────────┐
    │ │ │
    HashMap LinkedHashMap TreeMap
    Hashtable

    Common Map Methods

    code
    put()
    get()
    remove()
    containsKey()
    containsValue()
    keySet()
    values()
    entrySet()
    size()
    clear()
    isEmpty()

    Example:

    code
    Map<Integer,String> map =
    new HashMap<>();
    map.put(1,"Java");
    System.out.println(map.containsKey(1));

    Output:

    code
    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:

    code
    Map<String,String> capitals =
    new HashMap<>();
    capitals.put("India","New Delhi");
    capitals.put("Japan","Tokyo");

    Internal Working of HashMap

    Internally:

    code
    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:

    code
    map.put("Java",100);

    Steps:

    1. Compute hashCode()
    2. Calculate bucket index
    3. Check for existing key using equals()
    4. Replace value if key exists
    5. Otherwise create a new entry

    Flow:

    code
    Key
    hashCode()
    Bucket
    equals()
    Store

    Buckets

    Example:

    code
    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:

    code
    Key A
    Bucket 5
    Key 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:

    code
    Bucket
    Node
    Node
    Node
    Node

    After Java 8 (when thresholds are met):

    code
    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:

    code
    0.75

    Formula:

    code
    Current Entries
    ──────────────
    Capacity

    When the load factor threshold is exceeded, the HashMap grows and redistributes its entries.

    Rehashing (Resizing)

    Suppose capacity:

    code
    16

    When the threshold is exceeded:

    code
    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

    OperationAverageWorst 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:

    code
    Map<Integer,String> students =
    new LinkedHashMap<>();
    students.put(1,"Rahul");
    students.put(2,"Amit");
    students.put(3,"John");

    Output:

    code
    1=Rahul
    2=Amit
    3=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:

    code
    Map<Integer,String> marks =
    new TreeMap<>();
    marks.put(80,"A");
    marks.put(95,"A+");
    marks.put(70,"B");

    Output:

    code
    70=B
    80=A
    95=A+

    Characteristics:

    • Sorted keys
    • No null keys
    • O(log n) operations

    Hashtable

    Hashtable is a legacy synchronized implementation.

    Example:

    code
    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

    FeatureHashMapLinkedHashMapTreeMapHashtable
    OrderingNoneInsertionSorted KeysNone
    Null KeyOneOneNoNo
    Null ValuesYesYesYesNo
    Thread-SafeNoNoNoYes
    Average SearchO(1)O(1)O(log n)O(1)

    equals() and hashCode()

    For custom key classes:

    code
    class Employee {
    int id;
    String name;
    }

    Always override:

    code
    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():

    code
    for(Map.Entry<Integer,String> entry
    : employees.entrySet()){
    System.out.println(
    entry.getKey() + " : " +
    entry.getValue());
    }

    Using keySet():

    code
    for(Integer id : employees.keySet()){
    System.out.println(id);
    }

    Using values():

    code
    for(String name : employees.values()){
    System.out.println(name);
    }

    Real-World Example: Employee Directory

    code
    Map<Integer,String> employees =
    new HashMap<>();
    employees.put(101,"Rahul");
    employees.put(102,"Amit");
    employees.put(103,"Jagannath");

    Finding an employee:

    code
    System.out.println(
    employees.get(103));

    Real-World Example: Shopping Cart

    code
    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:

    1. Creates a HashMap of employee IDs and names.
    2. Demonstrates insertion, retrieval, update, and deletion.
    3. Iterates using:
      • entrySet()
      • keySet()
      • values()
    4. Creates a LinkedHashMap and verifies insertion order.
    5. Creates a TreeMap and verifies sorted keys.
    6. Creates a custom EmployeeKey class with proper equals() and hashCode().
    7. Demonstrates the effect of duplicate keys.
    8. Compares the behavior of HashMap, LinkedHashMap, TreeMap, and Hashtable.

    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

    • Map stores key-value pairs.
    • Keys are unique; values may be duplicated.
    • Map is not part of the Collection interface hierarchy.
    • HashMap provides fast average-case lookup using hashing.
    • LinkedHashMap preserves insertion order.
    • TreeMap maintains sorted keys.
    • Hashtable is synchronized but largely replaced by ConcurrentHashMap.
    • Proper equals() and hashCode() 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.

    Ready to mark this lesson complete?Track your journey across the entire course.