ZetCode

Java Collections.unmodifiableSortedMap

上次修改时间:2025 年 4 月 20 日

Collections.unmodifiableSortedMap 方法是 Java 集合框架的一部分。它提供了一个指定 SortedMap 的不可修改视图。此方法对于创建不可变的排序映射结构很有用。

不可修改的排序映射会阻止通过返回的视图修改底层映射。 任何尝试修改映射的操作都将导致 UnsupportedOperationException。 该视图保持原始映射的排序顺序。

SortedMap 接口概述

SortedMapMap 的一个子接口,它以键的升序维护其条目。 排序由键的自然排序或创建时提供的 Comparator 确定。

主要功能包括用于范围视图的方法(subMapheadMaptailMap)和对第一个/最后一个键的访问。 TreeMapSortedMap 的主要实现。

Basic unmodifiableSortedMap 用法

此示例演示了 Collections.unmodifiableSortedMap 的基本用法。 我们创建一个 TreeMap,然后获取其不可修改的视图。 该示例展示了成功的读取操作和失败的修改尝试。

BasicUnmodifiableSortedMap.java
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());
        }
    }
}

此代码创建一个包含一些条目的 TreeMap,然后将其包装在不可修改的视图中。 该视图允许所有读取操作,但会在任何修改尝试时抛出异常。 该输出展示了成功的读取和失败的写入。

不可修改的视图反映对原始映射的更改,但阻止通过自身进行直接修改。 这对于提供对排序映射数据的只读访问非常有用。

带有 Comparator 的不可修改 SortedMap

此示例展示了 unmodifiableSortedMap 如何使用自定义 Comparator。 我们创建一个具有降序的 TreeMap,然后使其不可修改。 该视图保持原始排序顺序。

ComparatorUnmodifiableSortedMap.java
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));
    }
}

此示例演示了不可修改的视图保留原始映射的排序顺序。 我们使用自定义 Comparator 创建了一个具有降序的 TreeMap,然后使其不可修改。

输出显示降序在不可修改的视图中得以保留。 对原始映射的更改会反映在视图中,但视图本身无法直接修改。

带有 Submaps 的不可修改 SortedMap

此示例探讨了 unmodifiableSortedMap 如何与子映射操作交互。 我们创建一个排序映射,获取一个不可修改的视图,然后使用其子映射视图。 所有派生视图都保持不可修改。

SubmapUnmodifiableSortedMap.java
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);
    }
}

此示例显示了对不可修改的排序映射执行子映射操作会返回不可修改的视图。 我们创建一个数字排序映射,使其不可修改,然后提取子映射、headmap 和 tailmap。

所有派生视图都继承了不可修改的特性。 尝试修改这些视图中的任何一个都会导致 UnsupportedOperationException。 这种行为确保了所有映射视图之间的一致不变性。

不可修改 SortedMap 性能

此示例演示了不可修改的排序映射的性能特征。 我们比较了常规 TreeMap 及其不可修改视图之间的操作。 包装器为读取操作增加了最小的开销。

PerformanceUnmodifiableSortedMap.java
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");
    }
}

此性能测试表明,不可修改的排序映射为读取操作增加了可忽略不计的开销。 我们比较了常规 TreeMap 及其不可修改视图之间的 get 操作和完整迭代。

结果表明,对于读取操作,不可修改的包装器不会显着影响性能。 小的开销来自包装器在操作期间的修改检查。

不可修改 SortedMap 序列化

此示例检查不可修改的排序映射的序列化行为。 我们序列化和反序列化 TreeMap 及其不可修改的视图。 该示例显示了如何保留不可修改的特性。

SerializationUnmodifiableSortedMap.java
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) {
        
        SortedMap prices = 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();
        }
    }
}

此示例演示了在序列化期间保留排序映射的不可修改特性。 我们序列化一个不可修改的视图,然后反序列化它并验证它是否保持不可修改。

输出显示反序列化的映射保持其不可修改的性质。 在反序列化后尝试修改它会导致预期的异常。 这种行为对于在进程边界之间保持不变性非常重要。

并发环境中的不可修改 SortedMap

此示例探讨了在并发编程中使用不可修改的排序映射。 虽然不可修改的映射对于读取是线程安全的,但我们检查了当底层映射同时更改时的行为。

ConcurrentUnmodifiableSortedMap.java
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);
    }
}

此示例演示了不可修改的排序映射对于并发读取是安全的,但反映了对底层映射的更改。 我们创建一个 TreeMap,使其不可修改,然后让两个线程:一个从不可修改的视图中读取,另一个修改原始映射。

读取器线程看到写入器线程所做的更改,这表明不可修改的视图是活动的并反映了底层更改。 为了确保线程安全,您需要同步对原始映射的访问或使用并发映射实现,例如 ConcurrentSkipListMap

真实用例:配置管理

此示例展示了 Collections.unmodifiableSortedMap 在配置管理系统中的实际应用。 我们创建一个类,该类提供对排序配置数据的只读访问,同时允许特权更新。

ConfigManagerUnmodifiableSortedMap.java
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);
    }
}

此示例演示了在配置管理器中使用 unmodifiableSortedMap。 该类维护配置属性的排序映射,并向客户端公开不可修改的视图。 授权更新通过 updateConfig 方法进行。

输出显示阻止了对视图的直接修改,但通过管理器进行的更新会反映在视图中。 这种模式对于维护具有受控访问权限的排序配置数据非常有用。

来源

Java Collections.unmodifiableSortedMap 文档

在本教程中,我们深入探讨了 Collections.unmodifiableSortedMap。 我们介绍了基本用法、自定义比较器、子映射操作、性能特征、序列化、并发访问以及配置管理中的实际用例。 此方法对于创建排序映射的只读视图同时保留其排序属性非常有用。

作者

我叫 Jan Bodnar,我是一位敬业的程序员,在该领域拥有多年的经验。 我于 2007 年开始撰写编程文章,此后撰写了超过 1,400 篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程