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 aNullPointerException
.
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 containnull
keys or values. If the stream containsnull
, aNullPointerException
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 usingCollectors.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 containsnull
keys or values.
By using Collectors.toUnmodifiableMap()
, you can create safe, immutable maps in your Java applications, making your code more robust and reliable.