1. 简介

很多开发者倾向于将应用配置参数放在源码之外,便于维护和部署。在 Java 中,一种常见的做法是使用外部配置文件,并通过 java.util.Properties 类来读取这些配置。

本篇文章将聚焦于**如何将 java.util.Properties 转换为 HashMap<String, String>**,我们会介绍几种不同的实现方式:纯 Java 实现、使用 Lambda 表达式的方式,以及借助第三方库(如 Guava)的实现。每种方式我们都会结合示例代码分析其优劣。

2. 使用 HashMap 构造函数转换

在开始编码前,我们先来看看 Properties 的 Javadoc。可以看到,Properties 类继承自 Hashtable<Object, Object>,同时也实现了 Map 接口。而且 Java 为 Properties 提供了专门处理字符串的 setProperty()getProperty() 方法。

基于这个信息,我们可以直接使用类型转换和构造函数的方式完成转换:

public static HashMap<String, String> typeCastConvert(Properties prop) {
    Map step1 = prop;
    Map<String, String> step2 = (Map<String, String>) step1;
    return new HashMap<>(step2);
}

这段代码分为三步:

  1. Properties 转换为原始类型 Map,这会触发一个编译警告(可以使用 @SuppressWarnings("rawtypes") 抑制);
  2. 再将原始 Map 转换为目标泛型 Map<String, String>,同样会有类型未检查警告(可用 @SuppressWarnings("unchecked") 处理);
  3. 最后通过拷贝构造函数创建 HashMap

✅ 优点:这是最快的转换方式
❌ 缺点:存在类型安全隐患,如果 Properties 中混入了非 String 类型的 key 或 value,后续使用时可能抛出 ClassCastException

比如:

properties.put("property4", 456);
properties.put(5, 10.11);

HashMap<String, String> hMap = typeCastConvert(properties);
assertThrows(ClassCastException.class, () -> {
    String s = hMap.get("property4");
});
assertEquals(Integer.class, ((Object) hMap.get("property4")).getClass());

assertThrows(ClassCastException.class, () -> {
    String s = hMap.get(5);
});
assertEquals(Double.class, ((Object) hMap.get(5)).getClass());

虽然转换过程没有报错,但最终 HashMap 中的值并非都是字符串。因此,即使这个方法看起来最简单,也要注意后续使用中的类型安全问题

3. 使用 Guava API 转换

如果你可以使用第三方库,Google 的 Guava 提供了一个便捷的方法:Maps.fromProperties()。它会返回一个 ImmutableMap<String, String>,我们可以再包装为 HashMap

public HashMap<String, String> guavaConvert(Properties prop) {
    return Maps.newHashMap(Maps.fromProperties(prop));
}

✅ 优点:类型安全更好,转换过程中如果遇到非字符串的 key 或 value 会直接抛出异常,提前暴露问题。
❌ 缺点:对非法值容忍度低,容易抛异常

示例:

properties.put("property4", 456);
assertThrows(NullPointerException.class, 
    () -> PropertiesToHashMapConverter.guavaConvert(properties));

properties.put(5, 10.11);
assertThrows(ClassCastException.class, 
    () -> PropertiesToHashMapConverter.guavaConvert(properties));

第一个异常是由于 Integer 类型值无法通过 getProperty() 获取导致的 NullPointerException;第二个则是 key 类型不合法导致的 ClassCastException

总的来说,Guava 的方式更安全但也更严格,适用于对配置数据类型有严格控制的场景。

4. 自定义类型安全实现

前面两种方式都存在一定的类型安全隐患或限制。现在我们来实现一个自定义的安全转换方法,手动控制类型转换逻辑。

4.1. 使用 for 循环遍历

public HashMap<String, String> loopConvert(Properties prop) {
    HashMap<String, String> retMap = new HashMap<>();
    for (Map.Entry<Object, Object> entry : prop.entrySet()) {
        retMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
    }
    return retMap;
}

这种方式通过遍历 PropertiesentrySet(),对每个 key 和 value 使用 String.valueOf() 转换为字符串。
✅ 优点:类型安全可控,可扩展性强
⚠️ 缺点:需要手动编写循环逻辑。

4.2. 使用 Stream + Collectors

Java 8 引入了 Stream API,我们可以用更现代的方式重写上述逻辑:

public HashMap<String, String> streamConvert(Properties prop) {
    return prop.entrySet().stream().collect(
      Collectors.toMap(
        e -> String.valueOf(e.getKey()),
        e -> String.valueOf(e.getValue()),
        (prev, next) -> next, HashMap::new
    ));
}

这段代码使用了 Collectors.toMap() 方法,逻辑与上面的 for 循环一致,但更简洁。

✅ 优点:代码更现代、可读性强,类型安全可控
⚠️ 缺点:相比类型转换和 Guava,性能略低,但通常可以忽略。

测试示例:

properties.put("property4", 456);
properties.put(5, 10.11);

HashMap<String, String> hMap1 = loopConvert(properties);
HashMap<String, String> hMap2 = streamConvert(properties);

assertDoesNotThrow(() -> {
    String s1 = hMap1.get("property4");
    String s2 = hMap2.get("property4");
});
assertEquals("456", hMap1.get("property4"));
assertEquals("456", hMap2.get("property4"));

assertDoesNotThrow(() -> {
    String s1 = hMap1.get("5");
    String s2 = hMap2.get("5");
});
assertEquals("10.11", hMap1.get("5"));
assertEquals("10.11", hMap2.get("5"));

assertEquals(hMap2, hMap1);

可以看到,两种方式都能正确处理非字符串类型的 key 和 value,最终都转换为字符串,且结果一致。

5. 总结

本文介绍了几种将 java.util.Properties 转换为 HashMap<String, String> 的常见方式:

方法 性能 类型安全 可控性
类型转换(Type Casting) ✅ 最快 ❌ 有风险 ❌ 无控制
Guava API ✅ 安全 ✅ 较好 ⚠️ 遇非法值抛异常
自定义实现(for / stream) ⚠️ 一般 ✅ 安全 ✅ 高

简单粗暴的类型转换虽然快,但容易埋坑;Guava 虽然安全但不灵活;自定义实现最稳妥,适用于对类型安全有严格要求的场景。选择哪种方式,取决于你对类型安全、性能和可维护性的权衡。


原始标题:Converting Java Properties to HashMap | Baeldung