ZetCode

Java Collections.singletonMap 方法

最后修改时间:2025 年 5 月 1 日

Collections.singletonMap 方法是 Java Collections 框架中一个有用的工具,它允许创建一个包含恰好一个键值对的不可变 Map。作为 java.util.Collections 类的一部分,它提供了一种简化的方法来处理单条目 Map,而无需使用完整的集合。

当需要一个单条目 Map 且不需要修改时,单例 Map 是理想的选择。它们具有很高的内存效率,并且本质上是线程安全的,这使得它们在并发环境中成为一个可靠的选择。自 Java 1.3 引入以来,singletonMap 一直是简化基于 Map 操作的常用工具。

Collections.singletonMap 概述

Collections.singletonMap 方法返回一个不可变且可序列化的 Map,其中包含单个键值对。虽然它实现了 Map 接口,但任何试图修改其内容(例如添加或删除元素)的操作都会导致 UnsupportedOperationException

singletonMap 的关键特征包括

由于其效率和简单性,singletonMap 常用在方法参数、API 调用以及倾向于不可变性而非动态修改的场景中。

基本 singletonMap 用法

此示例演示了 Collections.singletonMap 的最基本用法。我们创建一个包含一个键值对的 Map,并通过尝试修改它来演示其不可变性。

SingletonMapBasic.java
package com.zetcode;

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

public class SingletonMapBasic {

    public static void main(String[] args) {
        
        // Create singleton map
        Map<String, Integer> ageMap = Collections.singletonMap("John", 30);
        
        // Access elements
        System.out.println("John's age: " + ageMap.get("John"));
        System.out.println("Map size: " + ageMap.size());
        
        try {
            // Attempt to modify (will throw exception)
            ageMap.put("Alice", 25);
        } catch (UnsupportedOperationException e) {
            System.out.println("Expected exception: " + e.getMessage());
        }
    }
}

此代码创建一个单例 Map,其中 "John" 作为键,30 作为值。我们演示了访问值并检查 Map 大小。尝试添加另一个条目会抛出一个异常,证明了其不可变性。

输出显示了成功的读取操作,以及在尝试修改时预期的异常。此行为是单例集合的基础。

在 singletonMap 中使用 Null 值

此示例探讨了 singletonMap 如何处理空值。虽然键不能为 null,但如果值类型允许,值可以为 null。我们演示了这两种情况。

SingletonMapNulls.java
package com.zetcode;

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

public class SingletonMapNulls {

    public static void main(String[] args) {
        
        // Valid: null value
        Map<String, String> validMap = 
            Collections.singletonMap("config", null);
        System.out.println("Config value: " + validMap.get("config"));
        
        try {
            // Invalid: null key
            Map<String, String> invalidMap = 
                Collections.singletonMap(null, "value");
        } catch (NullPointerException e) {
            System.out.println("Expected exception: " + e.getMessage());
        }
    }
}

示例显示,虽然允许空值(当值类型允许时),但禁止空键。尝试使用空键会抛出 NullPointerException

此行为与 Java 中的一般 Map 契约相符,其中键不能为 null,但值可以为 null(除非由实现限制)。输出演示了这两种情况。

在方法参数中使用 singletonMap

Collections.singletonMap 方法提供了一种快速的方法,可以将单条目 Map 传递给需要 Map 参数的方法。当调用 API 或实用程序函数(不需要完整的 Map)时,这特别有用。在此示例中,我们模拟一个系统,该系统使用单例 Map 检索特定城市的天气数据。

SingletonMapParameter.java
package com.zetcode;

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

public class SingletonMapParameter {

    public static void displayWeatherInfo(Map<String, Integer> weatherData) {

        System.out.println("Weather Report:");
        weatherData.forEach((city, temp) ->
                System.out.println("The temperature in " + city + " is " + temp + "°C"));
        System.out.println("Total locations processed: " + weatherData.size());
    }

    public static void main(String[] args) {
        
        // Using singletonMap to pass a single city's weather data
        displayWeatherInfo(Collections.singletonMap("Bratislava", 18));

        // Alternative approach without singletonMap:
        // Map<String, Integer> weatherMap = new HashMap<>();
        // weatherMap.put("Bratislava", 18);
        // displayWeatherInfo(weatherMap);
    }
}

此示例突出显示了 singletonMap 如何简化将单值 Map 传递给方法的操作,从而减少冗余,同时保持效率。无需手动创建和填充可变 Map,单例 Map 提供了一种简化的替代方案。

这种模式被广泛应用于 Java 应用程序中,特别是在 API 调用、配置设置以及只需要一个键值对的场景中。输出演示了方法如何无缝地处理单例 Map。

将 singletonMap 与常规 Map 进行比较

此示例比较了 singletonMap 与常规 HashMap 的内存使用和性能特征。我们演示了内存占用和修改行为的差异。

SingletonMapComparison.java
package com.zetcode;

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

public class SingletonMapComparison {

    public static void main(String[] args) {
        
        // Singleton map
        Map<String, String> singleton = 
            Collections.singletonMap("id", "A100");
        
        // Regular HashMap
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("id", "A100");
        
        System.out.println("Singleton map class: " + singleton.getClass());
        System.out.println("HashMap class: " + hashMap.getClass());
        
        // Memory comparison
        System.out.println("\nSingleton map overhead: minimal");
        System.out.println("HashMap overhead: includes hash table structure");
        
        // Modification comparison
        try {
            singleton.put("newKey", "value");
        } catch (UnsupportedOperationException e) {
            System.out.println("\nCannot modify singleton map");
        }
        
        hashMap.put("newKey", "value");
        System.out.println("HashMap modified successfully");
    }
}

该示例突出了单例 Map 和常规 HashMap 之间的主要区别。单例 Map 的内存开销最小,因为它们不需要哈希表结构。它们是不可变的,而 HashMaps 默认是可变的。

输出显示了不同的行为,并提醒我们根据需求选择合适的 Map 类型。单例 Map 非常适合不可变的单条目情况。

在 Java 中使用 singletonMap 与自定义对象

Collections.singletonMap 方法允许创建一个包含单个键值对的不可变 Map。此示例演示了使用自定义对象作为键和值时的行为,特别是使用 Person 记录。我们将探讨对象引用在单例 Map 中的工作方式。

SingletonMapCustomObjects.java
package com.zetcode;

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

record Person(String name) { }

public class SingletonMapCustomObjects {

    public static void main(String[] args) {

        Person john = new Person("John Doe");
        Person jane = new Person("Jane Smith");

        Map<Person, Person> marriage =
                Collections.singletonMap(john, jane);

        System.out.println("Marriage mapping:");
        marriage.forEach((k, v) ->
                System.out.println(k.name() + " is married to " + v.name()));

        // Changing the reference doesn't affect the map
        john = new Person("Johnny Doe"); // New object, but map remains unchanged
        System.out.println("\nAfter modifying original reference:");
        marriage.forEach((k, v) ->
                System.out.println(k.name() + " is married to " + v.name()));
    }
}

此示例强调了 singletonMap 存储对象引用而不是副本。虽然我们将一个新的 Person 实例分配给 john 变量,但原始映射保持不变,因为不可变集合不会动态更新。

此行为在所有 Java 集合中都是一致的:修改引用不会改变已存储在集合中的对象。

Collections 框架中的 singletonMap

此示例显示了 singletonMap 如何融入更广泛的 Collections 框架。我们将把它与其他集合一起使用,并演示互操作性。

SingletonMapInFramework.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class SingletonMapInFramework {

    public static void main(String[] args) {
        
        // Create a list of singleton maps
        List<Map<String, Integer>> mapList = new ArrayList<>();
        
        mapList.add(Collections.singletonMap("Alice", 25));
        mapList.add(Collections.singletonMap("Bob", 30));
        mapList.add(Collections.singletonMap("Charlie", 35));
        
        System.out.println("List of singleton maps:");
        mapList.forEach(map -> 
            map.forEach((k, v) -> System.out.println(k + ": " + v)));
        
        // Extract all values
        List<Integer> ages = new ArrayList<>();
        mapList.forEach(map -> ages.addAll(map.values()));
        
        System.out.println("\nAll ages: " + ages);
    }
}

在这里,我们看到单例 Map 如何与其他集合类型一起使用。我们创建了一个单例 Map 的列表,每个 Map 代表一个人的年龄。然后,我们将所有值提取到单独的列表中。

这演示了单例 Map 与 Collections 框架其余部分的互操作性。输出显示了列表中所有 Map 的合并数据。

性能注意事项

此最终示例考察了 singletonMap 与常规 Map 相比的性能优势。我们将测量两者使用的内存和访问时间。

SingletonMapPerformance.java
package com.zetcode;

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

public class SingletonMapPerformance {

    public static void main(String[] args) {
        
        final int iterations = 10_000_000;
        
        // Test singleton map
        long startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            Map<Integer, String> map = Collections.singletonMap(i, "value");
        }
        long singletonTime = System.nanoTime() - startTime;
        
        // Test HashMap
        startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            Map<Integer, String> map = new HashMap<>();
            map.put(i, "value");
        }
        long hashMapTime = System.nanoTime() - startTime;
        
        System.out.println("SingletonMap creation time: " + 
            (singletonTime / 1_000_000) + " ms");
        System.out.println("HashMap creation time: " + 
            (hashMapTime / 1_000_000) + " ms");
        System.out.println("Ratio: " + 
            ((double)hashMapTime / singletonTime));
    }
}

性能测试表明,当您只需要一个条目时,singletonMap 的创建速度比 HashMap 快得多。它也使用更少的内存,因为它不分配哈希表。

输出演示了创建数百万个 Map 类型的时差。对于不可变性可接受的单条目情况,单例 Map 是最佳选择。

来源

Java Collections.singletonMap 文档

在本文中,我们深入探讨了 Java Collections.singletonMap 方法。我们涵盖了基本用法、空值处理、实际应用、性能以及与其他集合的集成。

作者

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

列出所有Java教程