Collectors.toUnmodifiableMap() in Java

Introduction

Java 10 introduced the Collectors.toUnmodifiableMap() method, which is used to collect elements from a stream into an unmodifiable map. This method is part of the Java Stream API and provides a way to create read-only maps directly from streams. Once created, the resulting map cannot be modified, ensuring data integrity and immutability.

Key Points:

  • Immutable Map: Collectors.toUnmodifiableMap() creates a map that cannot be changed.
  • Stream API: Used with streams to collect elements into an unmodifiable map.
  • Read-Only: Any attempt to modify the resulting map will throw an UnsupportedOperationException.
  • Null Safety: If the stream contains null keys or values, the collector will throw a NullPointerException.

How to Use Collectors.toUnmodifiableMap()

The Collectors.toUnmodifiableMap() method is used as a terminal operation in a stream pipeline to collect elements into an unmodifiable map.

Syntax:

Map<K, V> unmodifiableMap = stream.collect(Collectors.toUnmodifiableMap(keyMapper, valueMapper));
  • stream: A stream of elements to be collected.
  • keyMapper: A function that extracts keys from the stream elements.
  • valueMapper: A function that extracts values from the stream elements.
  • unmodifiableMap: The resulting read-only map.

Example of Using Collectors.toUnmodifiableMap()

Basic Example

Here’s a simple example demonstrating the use of Collectors.toUnmodifiableMap() with Indian cities and their populations:

import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class UnmodifiableMapExample {
    public static void main(String[] args) {
        // Create a stream of cities and their populations
        Stream<String[]> cityStream = Stream.of(
            new String[]{"Mumbai", "20 million"},
            new String[]{"Delhi", "18 million"},
            new String[]{"Bengaluru", "10 million"},
            new String[]{"Chennai", "8 million"}
        );

        // Collect the stream elements into an unmodifiable map
        Map<String, String> cityMap = cityStream.collect(
            Collectors.toUnmodifiableMap(
                city -> city[0], // Key Mapper: City Name
                city -> city[1]  // Value Mapper: Population
            )
        );

        System.out.println("Unmodifiable City Map: " + cityMap);

        // Attempt to modify the unmodifiable map
        try {
            cityMap.put("Hyderabad", "7 million");  // This will throw UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify cityMap: " + e.getMessage());
        }
    }
}

Output:

Unmodifiable City Map: {Mumbai=20 million, Delhi=18 million, Bengaluru=10 million, Chennai=8 million}
Cannot modify cityMap: null

Explanation:

  • Immutable Map: The Collectors.toUnmodifiableMap() method creates a map that cannot be modified, ensuring the map’s content is protected.
  • Error on Modification: Any attempt to modify the resulting map, such as adding or removing entries, will result in an UnsupportedOperationException.

Null Safety

The Collectors.toUnmodifiableMap() method does not allow null keys or values in the stream. If the stream contains null, a NullPointerException is thrown.

Example:

import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class NullSafetyExample {
    public static void main(String[] args) {
        // Create a stream with a null key
        Stream<String[]> cityStream = Stream.of(
            new String[]{"Mumbai", "20 million"},
            new String[]{null, "18 million"}, // Null key
            new String[]{"Bengaluru", "10 million"}
        );

        try {
            // Attempt to collect the stream into an unmodifiable map
            Map<String, String> cityMap = cityStream.collect(
                Collectors.toUnmodifiableMap(
                    city -> city[0], // Key Mapper: City Name
                    city -> city[1]  // Value Mapper: Population
                )
            );
        } catch (NullPointerException e) {
            System.out.println("Stream contains null keys: " + e.getMessage());
        }
    }
}

Output:

Stream contains null keys: null

Explanation:

  • No Null Keys or Values: The Collectors.toUnmodifiableMap() method ensures that the resulting map does not contain null keys or values. If the stream contains null, a NullPointerException is thrown.

Real-World Example

Using Collectors.toUnmodifiableMap() is beneficial when you want to create a map from a stream and ensure that the map remains unchanged.

Example with Fruit Prices:

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class FruitPriceExample {
    public static void main(String[] args) {
        // Create a list of fruits and their prices
        List<Fruit> fruits = List.of(
            new Fruit("Mango", 50),
            new Fruit("Banana", 20),
            new Fruit("Apple", 70),
            new Fruit("Orange", 60)
        );

        // Collect the list into an unmodifiable map with fruit names as keys and prices as values
        Map<String, Integer> fruitPriceMap = fruits.stream()
            .collect(Collectors.toUnmodifiableMap(
                Fruit::getName,    // Key Mapper: Fruit Name
                Fruit::getPrice    // Value Mapper: Fruit Price
            ));

        System.out.println("Unmodifiable Fruit Price Map: " + fruitPriceMap);

        // Attempt to modify the unmodifiable map
        try {
            fruitPriceMap.put("Grapes", 80);  // This will throw UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot modify fruitPriceMap: " + e.getMessage());
        }
    }
}

class Fruit {
    private final String name;
    private final int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }
}

Output:

Unmodifiable Fruit Price Map: {Mango=50, Banana=20, Apple=70, Orange=60}
Cannot modify fruitPriceMap: null

Explanation:

  • Stream Processing: The example collects a list of Fruit objects into an unmodifiable map using Collectors.toUnmodifiableMap().
  • Immutable Map: The resulting map is immutable, ensuring that the map remains unchanged.
  • Error on Modification: Any attempt to add an entry to the map results in an UnsupportedOperationException.

Conclusion

The Collectors.toUnmodifiableMap() method in Java 10 provides a simple way to create immutable maps from streams. This feature is particularly useful when you want to ensure that a map created from a stream remains read-only, enhancing the safety and reliability of your code.

Summary:

  • Immutable Map: Creates a map that cannot be modified.
  • Stream API: Collects elements from streams into an unmodifiable map.
  • Read-Only: The resulting map is read-only, preventing any modifications.
  • Null Safety: The collector will throw a NullPointerException if the stream contains null keys or values.

By using Collectors.toUnmodifiableMap(), you can create safe, immutable maps in your Java applications, making your code more robust and reliable.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top