Java Collections.unmodifiableSortedMap
Last modified: April 20, 2025
The Collections.unmodifiableSortedMap
method is part of Java's
Collections Framework. It provides an unmodifiable view of a specified
SortedMap
. This method is useful for creating immutable sorted map
structures.
An unmodifiable sorted map prevents modifications to the underlying map through
the returned view. Any attempt to modify the map will result in an
UnsupportedOperationException
. The view maintains the original
map's sorting order.
SortedMap Interface Overview
SortedMap
is a subinterface of Map
that maintains its
entries in ascending key order. The ordering is determined by the natural
ordering of keys or by a Comparator
provided at creation time.
Key features include methods for range views (subMap
,
headMap
, tailMap
) and access to first/last keys.
TreeMap
is the primary implementation of SortedMap
.
Basic unmodifiableSortedMap Usage
This example demonstrates the basic usage of
Collections.unmodifiableSortedMap
. We create a TreeMap
,
then obtain an unmodifiable view of it. The example shows both successful read
operations and failed modification attempts.
package com.zetcode; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; public class BasicUnmodifiableSortedMap { public static void main(String[] args) { // Create a mutable sorted map SortedMap<String, Integer> scores = new TreeMap<>(); scores.put("Alice", 85); scores.put("Bob", 92); scores.put("Charlie", 78); // Create unmodifiable view SortedMap<String, Integer> unmodifiableScores = Collections.unmodifiableSortedMap(scores); // Read operations work System.out.println("First key: " + unmodifiableScores.firstKey()); System.out.println("Alice's score: " + unmodifiableScores.get("Alice")); try { // Attempt modification throws exception unmodifiableScores.put("Diana", 88); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify: " + e.getMessage()); } } }
This code creates a TreeMap
with some entries, then wraps it in an
unmodifiable view. The view allows all read operations but throws an exception
on any modification attempt. The output demonstrates both successful reads and
failed writes.
The unmodifiable view reflects changes to the original map but prevents direct modifications through itself. This is useful for providing read-only access to sorted map data.
Unmodifiable SortedMap with Comparator
This example shows how unmodifiableSortedMap
works with a custom
Comparator
. We create a TreeMap
with descending order,
then make it unmodifiable. The view maintains the original sorting order.
package com.zetcode; import java.util.Collections; import java.util.Comparator; import java.util.SortedMap; import java.util.TreeMap; public class ComparatorUnmodifiableSortedMap { public static void main(String[] args) { // Create comparator for descending order Comparator<String> descending = Comparator.reverseOrder(); // Create sorted map with custom comparator SortedMap<String, Integer> products = new TreeMap<>(descending); products.put("Laptop", 999); products.put("Phone", 699); products.put("Tablet", 399); // Create unmodifiable view SortedMap<String, Integer> unmodifiableProducts = Collections.unmodifiableSortedMap(products); // View maintains original order System.out.println("Products in descending order:"); unmodifiableProducts.forEach((k, v) -> System.out.println(k + ": $" + v)); // Original map can still be modified products.put("Monitor", 249); System.out.println("\nAfter original modification:"); unmodifiableProducts.forEach((k, v) -> System.out.println(k + ": $" + v)); } }
This example demonstrates that the unmodifiable view preserves the original
map's sorting order. We created a TreeMap
with descending order
using a custom Comparator
, then made it unmodifiable.
The output shows the descending order is maintained in the unmodifiable view. Changes to the original map are reflected in the view, but the view itself cannot be modified directly.
Unmodifiable SortedMap with Submaps
This example explores how unmodifiableSortedMap
interacts with
submap operations. We create a sorted map, obtain an unmodifiable view, then
work with its submap views. All derived views remain unmodifiable.
package com.zetcode; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; public class SubmapUnmodifiableSortedMap { public static void main(String[] args) { SortedMap<Integer, String> numbers = new TreeMap<>(); numbers.put(1, "One"); numbers.put(2, "Two"); numbers.put(3, "Three"); numbers.put(4, "Four"); numbers.put(5, "Five"); SortedMap<Integer, String> unmodifiableNumbers = Collections.unmodifiableSortedMap(numbers); // Get submap (inclusive 2, exclusive 5) SortedMap<Integer, String> subMap = unmodifiableNumbers.subMap(2, 5); System.out.println("Submap (2-5): " + subMap); try { // Attempt to modify submap subMap.put(6, "Six"); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify submap: " + e.getMessage()); } // Head and tail maps are also unmodifiable SortedMap<Integer, String> headMap = unmodifiableNumbers.headMap(3); SortedMap<Integer, String> tailMap = unmodifiableNumbers.tailMap(3); System.out.println("Head map (<3): " + headMap); System.out.println("Tail map (>=3): " + tailMap); } }
This example shows that submap operations on an unmodifiable sorted map return unmodifiable views. We create a sorted map of numbers, make it unmodifiable, then extract a submap, headmap, and tailmap.
All derived views inherit the unmodifiable characteristic. Attempts to modify
any of these views result in UnsupportedOperationException
. This
behavior ensures consistent immutability across all map views.
Unmodifiable SortedMap Performance
This example demonstrates the performance characteristics of unmodifiable sorted
maps. We compare operations between a regular TreeMap
and its
unmodifiable view. The wrapper adds minimal overhead to read operations.
package com.zetcode; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; public class PerformanceUnmodifiableSortedMap { public static void main(String[] args) { // Create large sorted map SortedMap<Integer, String> bigMap = new TreeMap<>(); for (int i = 0; i < 100000; i++) { bigMap.put(i, "Value" + i); } // Create unmodifiable view SortedMap<Integer, String> unmodifiableBigMap = Collections.unmodifiableSortedMap(bigMap); // Time get operations long start = System.nanoTime(); bigMap.get(50000); long end = System.nanoTime(); System.out.println("Regular map get: " + (end - start) + " ns"); start = System.nanoTime(); unmodifiableBigMap.get(50000); end = System.nanoTime(); System.out.println("Unmodifiable map get: " + (end - start) + " ns"); // Time iteration start = System.nanoTime(); bigMap.forEach((k, v) -> {}); end = System.nanoTime(); System.out.println("\nRegular map iteration: " + (end - start) + " ns"); start = System.nanoTime(); unmodifiableBigMap.forEach((k, v) -> {}); end = System.nanoTime(); System.out.println("Unmodifiable map iteration: " + (end - start) + " ns"); } }
This performance test shows that unmodifiable sorted maps add negligible
overhead to read operations. We compare get operations and full iterations
between a regular TreeMap
and its unmodifiable view.
The results demonstrate that the unmodifiable wrapper doesn't significantly impact performance for read operations. The small overhead comes from the wrapper's modification checks during operations.
Unmodifiable SortedMap Serialization
This example examines serialization behavior of unmodifiable sorted maps. We
serialize and deserialize both a TreeMap
and its unmodifiable
view. The example shows how the unmodifiable characteristic is preserved.
package com.zetcode; import java.io.*; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; public class SerializationUnmodifiableSortedMap { public static void main(String[] args) { SortedMapprices = new TreeMap<>(); prices.put("Apple", 1.99); prices.put("Banana", 0.99); prices.put("Orange", 2.49); SortedMap unmodifiablePrices = Collections.unmodifiableSortedMap(prices); // Serialize try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("prices.ser"))) { oos.writeObject(unmodifiablePrices); System.out.println("Serialized unmodifiable sorted map"); } catch (IOException e) { e.printStackTrace(); } // Deserialize try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("prices.ser"))) { @SuppressWarnings("unchecked") SortedMap deserialized = (SortedMap ) ois.readObject(); System.out.println("Deserialized map: " + deserialized); try { deserialized.put("Grape", 3.99); } catch (UnsupportedOperationException e) { System.out.println("\nDeserialized map is still unmodifiable"); } } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
This example demonstrates that the unmodifiable characteristic of a sorted map is preserved during serialization. We serialize an unmodifiable view, then deserialize it and verify it remains unmodifiable.
The output shows that the deserialized map maintains its unmodifiable nature. Attempts to modify it after deserialization result in the expected exception. This behavior is important for maintaining immutability across process boundaries.
Unmodifiable SortedMap in Concurrent Context
This example explores using unmodifiable sorted maps in concurrent programming. While unmodifiable maps are thread-safe for reads, we examine their behavior when the underlying map changes concurrently.
package com.zetcode; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ConcurrentUnmodifiableSortedMap { public static void main(String[] args) throws InterruptedException { SortedMap<String, Integer> stock = new TreeMap<>(); stock.put("WidgetA", 100); stock.put("WidgetB", 150); SortedMap<String, Integer> unmodifiableStock = Collections.unmodifiableSortedMap(stock); ExecutorService executor = Executors.newFixedThreadPool(2); // Reader thread executor.submit(() -> { for (int i = 0; i < 5; i++) { System.out.println("Reader: " + unmodifiableStock); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); // Writer thread (modifies original map) executor.submit(() -> { try { Thread.sleep(50); // Let reader start first stock.put("WidgetC", 200); System.out.println("\nWriter added WidgetC"); Thread.sleep(100); stock.remove("WidgetA"); System.out.println("Writer removed WidgetA"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); System.out.println("\nFinal state: " + unmodifiableStock); } }
This example demonstrates that unmodifiable sorted maps are safe for concurrent
reads but reflect changes to the underlying map. We create a TreeMap
,
make it unmodifiable, and then have two threads: one reading from the unmodifiable
view and another modifying the original map.
The reader thread sees the changes made by the writer thread, demonstrating that
the unmodifiable view is live and reflects underlying changes. To ensure thread
safety, you would need to synchronize access to the original map or use a
concurrent map implementation like ConcurrentSkipListMap
.
Real-world Use Case: Configuration Management
This example shows a practical application of
Collections.unmodifiableSortedMap
in a configuration management
system. We create a class that provides read-only access to sorted configuration
data while allowing privileged updates.
package com.zetcode; import java.util.Collections; import java.util.SortedMap; import java.util.TreeMap; public class ConfigManagerUnmodifiableSortedMap { private SortedMap<String, String> config = new TreeMap<>(); private SortedMap<String, String> unmodifiableConfig = Collections.unmodifiableSortedMap(config); public ConfigManagerUnmodifiableSortedMap() { config.put("db.host", "localhost"); config.put("db.port", "3306"); config.put("app.name", "MyApp"); } public SortedMap<String, String> getConfig() { return unmodifiableConfig; } public void updateConfig(String key, String value) { config.put(key, value); } public static void main(String[] args) { ConfigManagerUnmodifiableSortedMap manager = new ConfigManagerUnmodifiableSortedMap(); SortedMap<String, String> configView = manager.getConfig(); System.out.println("Initial config: " + configView); try { configView.put("db.user", "admin"); } catch (UnsupportedOperationException e) { System.out.println("\nCannot modify config view directly"); } manager.updateConfig("db.user", "admin"); System.out.println("Updated config: " + configView); } }
This example demonstrates using unmodifiableSortedMap
in a
configuration manager. The class maintains a sorted map of configuration
properties and exposes an unmodifiable view to clients. Authorized updates go
through the updateConfig
method.
The output shows that direct modifications to the view are prevented, but updates through the manager are reflected in the view. This pattern is useful for maintaining sorted configuration data with controlled access.
Source
Java Collections.unmodifiableSortedMap Documentation
In this tutorial, we've explored Collections.unmodifiableSortedMap
in depth. We've covered basic usage, custom comparators, submap operations,
performance characteristics, serialization, concurrent access, and a practical
use case in configuration management. This method is valuable for creating
read-only views of sorted maps while preserving their ordering properties.
Author
List all Java tutorials.