ZetCode

Java Collections.checkedMap 方法

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

Collections.checkedMap 方法是 Java Collections 框架中的一个实用方法。 它返回指定 Map 的动态类型安全视图。 此视图确保仅添加正确类型的元素。

类型安全视图有助于在运行时捕获不正确的类型添加。 它们在处理旧代码或不受信任的输入时特别有用。 checked map 为类型违规抛出 ClassCastException

Collections.checkedMap 概述

checkedMap 方法是 java.util.Collections 类的一部分。 它接受一个 Map 和两个 Class 对象(用于键和值)作为参数。 返回的 Map 在运行时强制执行类型安全。

当您需要在可能被非泛型代码访问的集合中确保类型安全时,此方法很有用。 它提供了运行时类型检查,补充了编译时泛型检查。

checkedMap 的基本用法

此示例演示了 Collections.checkedMap 的基本用法。 我们创建一个常规的 HashMap,然后用 checked map 包装它。 checked map 确保只能添加 String 键和 Integer 值。

BasicCheckedMap.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class BasicCheckedMap {

    public static void main(String[] args) {
        
        Map<String, Integer> originalMap = new HashMap<>();
        originalMap.put("One", 1);
        originalMap.put("Two", 2);
        
        // Create checked map
        Map<String, Integer> checkedMap = 
            Collections.checkedMap(originalMap, String.class, Integer.class);
        
        // Valid additions
        checkedMap.put("Three", 3);
        System.out.println("Checked map: " + checkedMap);
        
        try {
            // Invalid key type
            checkedMap.put(10, 10);
        } catch (ClassCastException e) {
            System.out.println("\nCaught exception: " + e.getMessage());
        }
        
        try {
            // Invalid value type
            checkedMap.put("Four", "4");
        } catch (ClassCastException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
}

此代码展示了 Collections.checkedMap 如何强制执行类型安全。 有效的添加操作正常工作,但尝试添加错误类型的元素会抛出 ClassCastException。 checked map 包装了原始 Map。

输出演示了成功的操作和捕获的异常。 这显示了运行时类型检查的实际效果。 原始 Map 仍然可访问。

带有不同类型的 Checked Map

此示例展示了如何创建具有不同键和值类型的 checked map。 我们使用一个具有 Integer 键和 String 值的 map。 checked map 确保键和值的类型一致性。

DifferentTypesCheckedMap.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DifferentTypesCheckedMap {

    public static void main(String[] args) {
        
        Map<Integer, String> originalMap = new HashMap<>();
        originalMap.put(1, "Apple");
        originalMap.put(2, "Banana");
        
        // Create checked map with Integer keys and String values
        Map<Integer, String> checkedMap = 
            Collections.checkedMap(originalMap, Integer.class, String.class);
        
        // Valid operation
        checkedMap.put(3, "Cherry");
        System.out.println("Checked map: " + checkedMap);
        
        try {
            // Invalid key type
            checkedMap.put("Four", "Date");
        } catch (ClassCastException e) {
            System.out.println("\nCaught key exception: " + e.getMessage());
        }
        
        try {
            // Invalid value type
            checkedMap.put(4, 4.5);
        } catch (ClassCastException e) {
            System.out.println("Caught value exception: " + e.getMessage());
        }
    }
}

此示例演示了对键和值的类型检查。 checked map 确保键是 Integer,值是 String。 任何偏离这些类型的行为都会导致 ClassCastException

输出显示了成功的添加和捕获的异常。 这说明了 checked map 如何维护复杂类型组合的类型安全。

带有自定义对象的 Checked Map

此示例演示了将 Collections.checkedMap 与自定义对象一起使用。 我们定义了一个简单的 Person 类,并创建了一个 checked map,它强制使用 Person 键和 Double 值。 这显示了 checked map 如何与用户定义的类型一起使用。

CustomObjectsCheckedMap.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return name;
    }
}

public class CustomObjectsCheckedMap {

    public static void main(String[] args) {
        
        Map originalMap = new HashMap<>();
        originalMap.put(new Person("John"), 75.5);
        originalMap.put(new Person("Alice"), 62.3);
        
        // Create checked map with Person keys and Double values
        Map checkedMap = 
            Collections.checkedMap(originalMap, Person.class, Double.class);
        
        // Valid addition
        checkedMap.put(new Person("Bob"), 80.1);
        System.out.println("Checked map: " + checkedMap);
        
        try {
            // Invalid key type
            checkedMap.put("Eve", 55.7);
        } catch (ClassCastException e) {
            System.out.println("\nCaught key exception: " + e.getMessage());
        }
        
        try {
            // Invalid value type
            checkedMap.put(new Person("Charlie"), "70.2");
        } catch (ClassCastException e) {
            System.out.println("Caught value exception: " + e.getMessage());
        }
    }
}

此示例展示了 checked map 如何使用自定义类强制执行类型安全。 Person 类实例必须用作键,并且只允许使用 Double 值。 尝试使用不正确的类型会导致异常。

输出演示了成功的操作和类型违规。 这说明了 checked map 如何与任何对象类型一起使用,而不仅仅是 Java 的内置类型。

带有 Null 值的 Checked Map

此示例探讨了 Collections.checkedMap 如何处理 null 键和值。 虽然 map 本身可能允许 null 值,但 checked map 的行为取决于原始 map 的实现。 我们演示了这两种情况。

NullValuesCheckedMap.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class NullValuesCheckedMap {

    public static void main(String[] args) {
        
        Map<String, Integer> originalMap = new HashMap<>();
        originalMap.put("One", 1);
        originalMap.put(null, 0); // HashMap allows null key
        
        // Create checked map
        Map<String, Integer> checkedMap = 
            Collections.checkedMap(originalMap, String.class, Integer.class);
        
        // Null key is allowed (depends on original map)
        checkedMap.put(null, 2);
        System.out.println("Map with null key: " + checkedMap);
        
        // Null value is allowed
        checkedMap.put("Two", null);
        System.out.println("Map with null value: " + checkedMap);
        
        try {
            // Attempt to put wrong type where null is expected
            checkedMap.put("Three", "3");
        } catch (ClassCastException e) {
            System.out.println("\nCaught exception: " + e.getMessage());
        }
    }
}

此示例演示了 checked map 中 null 处理依赖于原始 map 的实现。 HashMap 允许 null 键和值,因此 checked map 继承了此行为。 但是,类型检查仍然适用于非 null 值。

输出显示了成功的 null 操作和捕获的类型异常。 这说明了 null 值绕过了类型检查,但其他值受到严格执行。

Checked Map 性能考量

此示例演示了使用 checked map 的性能影响。 虽然它们提供了运行时类型安全,但它们为 map 操作增加了开销。 我们比较了常规 map 和 checked map 上的操作。

CheckedMapPerformance.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class CheckedMapPerformance {

    public static void main(String[] args) {
        
        final int SIZE = 100000;
        Map<Integer, String> regularMap = new HashMap<>();
        Map<Integer, String> checkedMap = 
            Collections.checkedMap(new HashMap<>(), Integer.class, String.class);
        
        // Test regular map put performance
        long start = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            regularMap.put(i, "Value " + i);
        }
        long regularPutTime = System.currentTimeMillis() - start;
        
        // Test checked map put performance
        start = System.currentTimeMillis();
        for (int i = 0; i < SIZE; i++) {
            checkedMap.put(i, "Value " + i);
        }
        long checkedPutTime = System.currentTimeMillis() - start;
        
        System.out.println("Regular map put time: " + regularPutTime + "ms");
        System.out.println("Checked map put time: " + checkedPutTime + "ms");
        System.out.println("Overhead: " + 
            (100.0 * (checkedPutTime - regularPutTime) / regularPutTime) + "%");
    }
}

此代码测量了常规 map 和 checked map 之间的性能差异。 checked map 在每次插入时执行类型检查,增加了开销。 确切的开销取决于 JVM 实现和系统特性。

输出显示了两种 map 类型之间的时间差。 虽然单个操作的开销通常很小,但在具有许多操作的性能关键代码中,它可能会变得很大。

Checked Map 在旧代码集成中的应用

此示例展示了 Collections.checkedMap 如何帮助将旧代码与泛型代码集成。 我们模拟一个返回原始 map 的旧方法,并展示如何安全地将其与泛型代码一起使用。

LegacyIntegration.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class LegacyIntegration {

    // Simulates legacy method returning raw map
    @SuppressWarnings("rawtypes")
    public static Map getLegacyMap() {
        Map map = new HashMap();
        map.put("A", 1);
        map.put("B", 2);
        // Legacy code might put wrong types
        map.put("C", "Three"); // Oops, wrong type!
        return map;
    }
    
    public static void main(String[] args) {
        
        // Get raw map from legacy code
        Map rawMap = getLegacyMap();
        
        try {
            // Try to use it directly (unsafe)
            @SuppressWarnings("unchecked")
            Map<String, Integer> unsafeMap = rawMap;
            System.out.println("Unsafe map: " + unsafeMap);
            
            // This would fail at runtime when accessing "C"
            for (Map.Entry<String, Integer> entry : unsafeMap.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        } catch (ClassCastException e) {
            System.out.println("\nCaught unsafe access exception: " + e.getMessage());
        }
        
        // Safe approach with checked map
        try {
            Map<String, Integer> safeMap = 
                Collections.checkedMap(new HashMap<>(), String.class, Integer.class);
            safeMap.putAll(rawMap); // Fails immediately on wrong type
        } catch (ClassCastException e) {
            System.out.println("Caught checked map exception: " + e.getMessage());
        }
    }
}

此示例演示了 checked map 如何帮助识别与旧代码一起使用时的类型问题。 不安全的方法在访问错误的条目时失败,而 checked map 在尝试将错误数据 putAll 时立即失败。

输出显示了不安全代码的延迟失败和 checked map 方法的立即失败。 这说明了 checked map 如何使类型问题更容易诊断。

带有并发访问的 Checked Map

此示例探讨了 checked map 在并发场景中的行为。 尽管 Collections.checkedMap 不提供线程安全性,但它可以与同步包装器结合使用,以实现类型安全的并发访问。

ConcurrentCheckedMap.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ConcurrentCheckedMap {

    public static void main(String[] args) {
        
        // Create thread-safe checked map
        Map<String, Integer> safeMap = Collections.synchronizedMap(
            Collections.checkedMap(new HashMap<>(), String.class, Integer.class));
        
        // Worker thread that adds to the map
        Thread worker = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                safeMap.put("Thread-" + i, i);
            }
        });
        
        // Main thread adds to the map
        worker.start();
        for (int i = 5; i < 10; i++) {
            safeMap.put("Main-" + i, i);
        }
        
        try {
            worker.join();
            System.out.println("Final map: " + safeMap);
            
            // Attempt concurrent modification with wrong type
            Thread badWorker = new Thread(() -> {
                try {
                    safeMap.put(10, 10); // Wrong key type
                } catch (ClassCastException e) {
                    System.out.println("Caught in thread: " + e.getMessage());
                }
            });
            
            badWorker.start();
            badWorker.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了如何将 Collections.checkedMapCollections.synchronizedMap 结合使用,以实现类型安全和线程安全性。 多个线程可以安全地向 map 添加元素,并且即使在并发场景中也会捕获类型违规。

输出显示了并发添加后的最终 map 内容以及当线程尝试添加错误类型的元素时捕获的异常。 这说明了 checked map 在多线程环境中的稳健性。

实际用例

此示例演示了 Collections.checkedMap 在管理键值对的配置服务中的实际应用。 该服务使用 checked map 来确保仅存储有效的配置类型。

ConfigurationService.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ConfigurationService {

    private final Map<String, Integer> configMap;
    
    public ConfigurationService() {
        Map<String, Integer> tempMap = new HashMap<>();
        configMap = Collections.checkedMap(tempMap, String.class, Integer.class);
    }
    
    public void setConfig(String key, Integer value) {
        configMap.put(key, value);
    }
    
    public Map<String, Integer> getConfig() {
        return Collections.unmodifiableMap(configMap);
    }
    
    public static void main(String[] args) {
        ConfigurationService service = new ConfigurationService();
        
        // Valid configurations
        service.setConfig("port", 8080);
        service.setConfig("timeout", 30);
        service.setConfig("maxConnections", 100);
        
        System.out.println("Configuration: " + service.getConfig());
        
        try {
            // Invalid configuration type
            service.getConfig().put("invalid", "value");
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify config directly: " + e.getMessage());
        }
        
        try {
            // Invalid value type
            Map<String, Integer> rawConfig = service.getConfig();
            rawConfig.put("retry", "5");
        } catch (ClassCastException e) {
            System.out.println("Caught invalid type: " + e.getMessage());
        }
    }
}

此示例显示了一个 ConfigurationService,它使用 checked map 来确保所有配置键都是字符串,并且值是整数。 该服务返回 map 的不可修改视图,以防止外部修改,并且 checked map 确保所有操作的类型安全。

输出演示了有效的配置设置,尝试修改返回的 map(失败)以及尝试添加无效类型(也失败)。 这种模式在必须严格类型化配置数据并防止外部更改的应用程序中很常见。

来源

Java Collections.checkedMap 文档

在本教程中,我们深入探讨了 Collections.checkedMap。 我们介绍了基本用法、不同的类型组合、自定义对象、null 处理、性能考量、旧代码集成、并发访问和实际应用。 此方法对于确保 map 操作的类型安全非常有用,尤其是在使用非泛型或不受信任的代码时。

作者

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

列出所有Java教程