Mastering Java Map Concepts: Tricky and Scenario-Based Interview Questions on HashMap, ConcurrentHashMap, SynchronizedMap, and TreeMap.

Table of contents

Introduction

In Java interviews, questions about the Map interface and its implementations like HashMap, ConcurrentHashMap, SynchronizedMap, and TreeMap often test both theoretical knowledge and practical problem-solving skills. This guide provides a mix of tricky and scenario-based questions with answers to help you ace your interview.


Tricky Questions and Answers

1. What happens if two keys have the same hash code in a HashMap?

  • Answer:
    If two keys have the same hash code, a collision occurs. HashMap uses a linked list or binary tree (after Java 8) at the bucket location to store these entries. The entries are stored as nodes, and their equality is determined by the equals() method, not just the hash code.

2. How does HashMap handle null keys and values?

  • Answer:

    • Null Key: HashMap allows a single null key and stores it at index 0 of the bucket array.

    • Null Values: Multiple null values are allowed.


3. Why is ConcurrentHashMap preferred over SynchronizedMap in multi-threaded environments?

  • Answer:

    • SynchronizedMap synchronizes all methods, which leads to a performance bottleneck.

    • ConcurrentHashMap uses a lock-striping mechanism, allowing concurrent reads and writes to different buckets, improving performance.


4. Can a TreeMap store null keys or values?

  • Answer:

    • Null keys are not allowed in TreeMap because it uses natural ordering or a comparator, and null keys can't be compared.

    • Null values are allowed.


5. What is the time complexity of get() and put() operations in HashMap?

  • Answer:

    • Best Case: O(1)O(1)O(1) if there are no collisions.

    • Worst Case: O(log⁡n)O(\log n)O(logn) in Java 8 and later (when buckets use balanced trees).


Scenario-Based Questions and Answers

1. Scenario: You are designing a cache system that needs frequent updates and concurrent reads/writes. Which map implementation would you choose?

  • Answer:
    Use ConcurrentHashMap because it supports high concurrency and avoids thread contention during reads and writes.

2. Scenario: You need to maintain a sorted list of employee IDs in ascending order. Which Map should you use?

  • Answer:
    Use a TreeMap, as it keeps the keys sorted in natural or custom order defined by a Comparator.

3. Scenario: Your application needs a synchronized Map but also demands frequent iterations. Which implementation is better?

  • Answer:
    Use Collections.synchronizedMap(new HashMap<>()). However, ensure proper external synchronization when iterating to avoid ConcurrentModificationException. For better performance, consider using ConcurrentHashMap.

4. Scenario: You have a HashMap storing sensitive data. How would you ensure thread safety while maintaining efficient performance?

  • Answer:
    Use ConcurrentHashMap for thread safety. If sensitive data requires stricter control, implement custom locking or atomic operations for specific use cases.

Advanced Tricky Questions

1. How does resizing work in HashMap?

  • Answer:

    • When the load factor exceeds the threshold, HashMap resizes by doubling the capacity.

    • During resizing, the entries are rehashed and redistributed into the new bucket array.


2. What is the difference between fail-fast and fail-safe iterators in the context of Map?

  • Answer:

    • Fail-Fast: Iterators on HashMap, SynchronizedMap, etc., throw a ConcurrentModificationException if the map is structurally modified during iteration.

    • Fail-Safe: Iterators on ConcurrentHashMap work on a copy of the map and do not throw exceptions during concurrent modifications.


3. Why is the initial capacity and load factor important in HashMap?

  • Answer:

    • Initial capacity reduces the need for resizing if the number of elements is predictable.

    • Load factor balances memory usage and performance. A higher load factor uses less memory but increases collision probability.


Coding Examples

1. Example: Using ConcurrentHashMap for Thread Safety

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        map.put("1", "Value1");
        map.put("2", "Value2");

        // Concurrently modify map
        map.putIfAbsent("3", "Value3");
        map.compute("2", (key, val) -> val + "_Modified");

        System.out.println(map);
    }
}

2. Example: Using TreeMap with Custom Comparator

import java.util.*;

public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<Integer, String> treeMap = new TreeMap<>(Comparator.reverseOrder());
        treeMap.put(1, "One");
        treeMap.put(2, "Two");
        treeMap.put(3, "Three");

        System.out.println(treeMap); // Prints in descending order
    }
}

Stream and Map Tricky Questions and Answers

1. How do you filter entries in a Map using Streams?

  • Answer:
    Use the entrySet() method to create a stream of Map.Entry<K, V> and apply filters.

  • Example:

      Map<String, Integer> map = Map.of("A", 10, "B", 20, "C", 30);
      Map<String, Integer> filteredMap = map.entrySet().stream()
          .filter(entry -> entry.getValue() > 15)
          .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
      System.out.println(filteredMap); // Output: {B=20, C=30}
    

2. How can you sort a Map by its values using Streams?

  • Answer:
    Convert the entrySet to a stream, sort it using a comparator, and collect it back into a new map.

  • Example:

      Map<String, Integer> map = Map.of("A", 30, "B", 10, "C", 20);
      Map<String, Integer> sortedMap = map.entrySet().stream()
          .sorted(Map.Entry.comparingByValue())
          .collect(Collectors.toMap(
              Map.Entry::getKey, 
              Map.Entry::getValue, 
              (e1, e2) -> e1, 
              LinkedHashMap::new));
      System.out.println(sortedMap); // Output: {B=10, C=20, A=30}
    

3. How do you convert a List to a Map using Streams?

  • Answer:
    Use Collectors.toMap() to transform a list into a map.

  • Example:

      List<String> names = List.of("Alice", "Bob", "Charlie");
      Map<String, Integer> nameLengthMap = names.stream()
          .collect(Collectors.toMap(name -> name, name -> name.length()));
      System.out.println(nameLengthMap); // Output: {Alice=5, Bob=3, Charlie=7}
    

4. What happens if duplicate keys are encountered while collecting a Stream into a Map?

  • Answer:
    A DuplicateKeyException is thrown unless a merge function is specified.

  • Example:

      List<String> names = List.of("Alice", "Bob", "Alice");
      Map<String, Integer> nameCount = names.stream()
          .collect(Collectors.toMap(
              name -> name, 
              name -> 1, 
              Integer::sum)); // Merges duplicate keys
      System.out.println(nameCount); // Output: {Alice=2, Bob=1}
    

5. How do you group a List into a Map using Streams?

  • Answer:
    Use Collectors.groupingBy() to group elements based on a classifier function.

  • Example:

      List<String> names = List.of("Alice", "Bob", "Charlie", "Anna");
      Map<Character, List<String>> groupedByInitial = names.stream()
          .collect(Collectors.groupingBy(name -> name.charAt(0)));
      System.out.println(groupedByInitial); // Output: {A=[Alice, Anna], B=[Bob], C=[Charlie]}
    

Scenario-Based Questions

1. Scenario: You have a Map of products and their prices. You need to create a new Map that applies a discount to all products costing more than $50.

  • Solution:

      Map<String, Double> products = Map.of("Laptop", 800.0, "Mouse", 20.0, "Keyboard", 100.0);
      Map<String, Double> discountedProducts = products.entrySet().stream()
          .map(entry -> entry.getValue() > 50 
              ? new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue() * 0.9) 
              : entry)
          .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
      System.out.println(discountedProducts); // Output: {Laptop=720.0, Mouse=20.0, Keyboard=90.0}
    

2. Scenario: Combine two Maps into one, summing the values of matching keys.

  • Solution:

      Map<String, Integer> map1 = Map.of("A", 10, "B", 20, "C", 30);
      Map<String, Integer> map2 = Map.of("B", 15, "C", 25, "D", 35);
      Map<String, Integer> combinedMap = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
          .collect(Collectors.toMap(
              Map.Entry::getKey, 
              Map.Entry::getValue, 
              Integer::sum));
      System.out.println(combinedMap); // Output: {A=10, B=35, C=55, D=35}
    

3. Scenario: Convert a Map to a List of strings in the format "key=value".

  • Solution:

      Map<String, Integer> map = Map.of("A", 10, "B", 20, "C", 30);
      List<String> list = map.entrySet().stream()
          .map(entry -> entry.getKey() + "=" + entry.getValue())
          .collect(Collectors.toList());
      System.out.println(list); // Output: [A=10, B=20, C=30]
    

4. Scenario: Partition a Map into two groups based on a condition.

  • Solution:

      Map<String, Integer> scores = Map.of("Alice", 85, "Bob", 65, "Charlie", 95);
      Map<Boolean, Map<String, Integer>> partitioned = scores.entrySet().stream()
          .collect(Collectors.partitioningBy(
              entry -> entry.getValue() >= 70, 
              Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
      System.out.println(partitioned); 
      // Output: {true={Alice=85, Charlie=95}, false={Bob=65}}
    

Final Note

Using Maps with Streams provides a concise and elegant way to perform operations that would otherwise require verbose loops. The examples and questions above should prepare you to confidently discuss and demonstrate your knowledge of Maps and Streams in any interview.

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language

0
Subscribe to my newsletter

Read articles from Maheshwar Ligade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maheshwar Ligade
Maheshwar Ligade

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI