如何将HashMap<K, V>的值类型从Object更改为String?

3
最简单/最好的转换方式是什么?
Map<String, Object>

为了

HashMap<String, String>

我正在使用的API有一些返回Map的方法,但是每次都需要将Object强制转换为String,这样做很麻烦。另外,这样做值得吗?HashMap是否比Map更快/更高效?我想我需要遍历原始Map并将值复制到新的HashMap中。谢谢!

1
一个Map是一个接口,所以你不能确定其他的Map实现是否比Map更快。 为什么要将对象强制转换为字符串?这个对象本身真的是一个字符串吗?还是在执行toString()方法? - Phuong Nguyen
向下转型可能非常危险。HashMap是Map,因此将(Map)myHashMap强制转换始终是可以的。将(HashMap)myMap强制转换并不总是正确的。这似乎是一个坏主意。 - Brad
该对象是一个字符串,但API并不明确,因此人们可以存储其他类型的对象。如果两者都是映射..那么为什么我要使用HashMap而不是Map对象呢? - Connor
我知道这似乎是个不好的想法,但对于我的项目来说并不是什么大问题,因为我确信它可以是一个HashMap。我的主要问题是,为什么我应该使用HashMap而不是Map?它会更快吗? - Connor
3个回答

10

您可以像其他人提到的那样使用构造函数:

Map<String, String> newMap = new HashMap(oldMap);

然而,这仅在您知道涉及的对象确实String时才有效。

但是,有一件事情我应该提一下:

不要将接口混淆。 Map只是一个接口;一个包含仅定义的合同。另一方面,接口的具体实现。因此,如果您使用Map 接口或其运行时类型(HashMap)在性能方面并没有任何区别。但是,如果您交换实现(例如使用TreeMap),可能会有所不同。

编辑:

下面是EE人员喜欢的详细解决方案(不涉及强制转换/原始类型警告):

public class MapConverter {
    public Map<String, String> convert(Map<String, Object> oldMap) {
        Map<String, String> ret = new HashMap<String, String>();
        for (String key : oldMap.keySet()) {
            ret.put(key, oldMap.get(key).toString());
        }
        return ret;
    }
}

好的,这很有道理。但我的主要问题是,是否有一种方法可以将 Map<String, Object> 转换为 Map<String, String>?我存储的值是字符串,我宁愿在此处转换类型,而不是每次检索值时都进行转换。 - Connor
为什么你一开始不使用String,而是使用Object - Adam Arold
2
@AdamArold 因为它是从外部库返回的,而不是他自己的代码。 - f1sh
1
+1 对于关于接口和实现之间差异的评论以及提到我的解决方案的提及表示赞同。 - mmirwaldt
谢谢!我选择了后一种解决方案。 :) - Connor

2

在原始类型上使用复制构造函数是有效的:

HashMap<String, String> hashMap = new HashMap(map);

然而,这个解决方案很丑陋,因为类型系统被忽略了。
编辑1:
当你执行
public static void main(String[] args) throws IllegalArgumentException,
        InterruptedException, IOException {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("Bla", new Object());
    HashMap<String, String> hashMap = new HashMap(map);
    System.out.println(hashMap.get("Bla").getClass());
}

如果您遇到"Class Cast Exception"异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String

执行"System.out.println(hashMap.get("Bla").getClass());"时会抛出此异常。

因此,转换实际上是被延迟的。

编辑2:

您可以通过以下方法避免拷贝

HashMap<String, String> hashMap = (HashMap)map;

然而,问题仍然存在,就像以下代码所示:
public static void main(String[] args) throws IllegalArgumentException,
        InterruptedException, IOException {
    HashMap<String, Object> oldMap = new HashMap<String, Object>();
    oldMap.put("Bla", new Object());
    HashMap<String, String> hashMap = (HashMap)oldMap;
    System.out.println(hashMap.get("Bla").getClass());
}

这段代码与EDIT1中的示例类似。

EDIT3: 使用lambda表达式怎么样?

    Map<String, Object> map = new HashMap<String, Object>();

    // 1

    final Stream<Map.Entry<String, Object>> entries = map.entrySet()
            .stream();

    final Function<Map.Entry<String, Object>, String> keyMapper = (
            Map.Entry<String, Object> entry) -> entry.getKey();

    final Function<Map.Entry<String, Object>, String> valueMapper = (
            Map.Entry<String, Object> entry) -> {
        final Object value = entry.getValue();
        if (value instanceof String) {
            return (String) value;
        } else {
            throw new ClassCastException("Value '" + value + "' of key '"
                    + entry.getKey() + "' cannot be cast from type "
                    + ((value != null) ? value.getClass().getName() : null)
                    + " to type " + String.class.getName());
        }
    };

    final BinaryOperator<String> duplicateHandler = (key1, key2) -> {
        throw new IllegalStateException(String.format("Duplicate key %s",
                key1));
    };

    final HashMap<String, String> hashMap = entries.collect(Collectors
            .toMap(keyMapper, valueMapper, duplicateHandler, HashMap::new));

    System.out.println(hashMap);

如果地图只有字符串到字符串的条目,它将复制它们所有。例如,插入。
    map.put("aKey", "aValue");

在评论1处。它将会打印输出。

    {aKey=aValue}

这很好。

如果你的映射中至少有一个字符串到非字符串的条目,复制操作将失败。

例如,插入

    map.put("aKey", 42);

在评论1中,它会打印出来。

Exception in thread "main" java.lang.ClassCastException: Value '42' of key ' aKey' cannot be cast from type java.lang.Integer to type java.lang.String
    at ...

这显示了字符串到非字符串的条目。

我知道这个解决方案并不简单,但它是安全的。


1
当然,“map.toString()” 的输出(这在“println”调用中隐式使用)在内部调用了该值的“toString()”方法。这与值getter的返回类型无关,而OP的问题正是关于它。 - f1sh

0

如果您知道键和值的类型(例如<String,String>),则可以直接将整个映射强制转换:

Map<String, String> newMap = (HashMap<String, String>)oldMap;

如果您需要一个单独的Map实例,可以像这样使用HashMap的构造函数:
HashMap<String, String> = new HashMap<String, String>((HashMap<String, String>) oldMap);

2
编译器显示“无法将HashMap <String,Object>转换为HashMap<String,String>” - mmirwaldt
我卡在了一个 Map<String, Object> 上,我想将值类型更改为 String(而不是 Object)。 - Connor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接