Java - 转换Map类型

27

如何以最简单的方式将 Map<Object,Object> 转换为 Map<String,String>

是否有一种不迭代遍历映射的方法来实现这一点?

谢谢


3
为什么一个 Map<String, String> 要先声明为 Map<Object, Object> - BalusC
1
这只是一个抽象的例子,它可以是将Map<Integer, String>转换为Map<Long, String>。 - Mark
2
如果你的确需要后者,那么你就必须循环。但是如果它实际上是一个 Map<String, String>,而被错误地声明为 Map<Object, Object>,那么你就可以直接使用 (Map) 进行强制转换(不需要泛型类型参数)。 - BalusC
你似乎在类型转换和数据转换之间感到困惑。类型转换只是告诉编译器已经存在的事实,而数据转换则是对数据进行操作。 - user207421
有些情况下,对象地图是适当的,但这只是例外而不是规律。 - Jay
5个回答

14

实际答案是:

Map<Object,Object> valueMap = ...;
@SuppressWarnings("unchecked")
Map<String,String> targetMap = (Map)valueMap;

5
因为那个无法编译。 - BalusC
哦,哇,它没有。谢谢BalusC。好吧,我编辑了我的片段,加入了一些肮脏的黑客技巧 :-) - Chris J
1
为什么 (Map)valueMap 能够工作,但 (Map<String,String>) 却不能? - paul

12
我认为解释为什么简单的解决方案不起作用,以及为什么你永远不应该使用它是个好主意。
假设您可以将List<Object>转换为List<String>(对于Map也适用,只是接口更简单)。从以下代码中,您会期望发生什么事情:
List<Object> m = Something;
m.add("Looks good.");
m.add(42);
List<String> s = (List<String>)m; // uhuh, no we don't want that.
String myString = s.get(1); // huh exception here.

现在你确实可以使用Bohemians/Chris的解决方案来进行黑客攻击,但基本上会破坏Java的类型系统。 不要这样做。 你不希望一个List<String>包含一个整数!以后调试时会很麻烦 - 循环遍历所有变量的额外代码会避免许多头痛,并且几乎不会影响性能。
如果有理由将Map声明为采用Object而不是String,则可能会向其中添加任何对象 - 通常您应该能够通过更好的泛型避免这种情况。

1
更多实际的例子。看看 java.util.Properties extends Hashtabl<Object,Object>,但你想要使用 Map<String,String> 而不是使用 Properties 的 getProperty 方法。所以你必须先进行转换。 - takacsot
我非常喜欢你的回答风格...让我想起了我如何处理许多问题;-) - GhostCat
@deathangel 不,你应该这样做以避免破坏类型系统。 - Voo
我在映射案例中该怎么做?stringList.stream().map(e -> (Object) e).collect(toList())?如果我只是将List<String>强制转换为List<Object>,会得到什么异常?每个规则都有例外。 - deathangel908
通常情况下,您无法控制返回地图(或列表)的API。我正在使用GAE memcache,它从getAll()返回Map<String,Object>。我将对象放在那里,所以我知道它们是<String,String>。有时需要进行强制转换以避免不必要的大型地图副本。这不就是强制转换的目的吗?为什么编译器在这种情况下会抱怨呢? - paul
显示剩余3条评论

9

如果你想要一些干净的东西,你必须“深入”地转换键和值。

使用Java 8流,这只需要几行代码:

static public Map<String, String> toStringString(Map<? extends Object, ? extends Object> map) {
        return map
                .entrySet()
                .stream()
                .collect(Collectors.toMap(
                    e -> e.getKey().toString(),
                    e -> e.getValue().toString()
                ));
    }

0
 public  Map<String, String> castOnlyStringValues(final Map<String, Object> map) {
    return map.entrySet().stream()
              .filter(x -> String.class.isAssignableFrom(x.getValue().getClass()))
              .map(x -> (Entry<?, ?>) x).map(x -> (Entry<String, String>) x)
              .collect(toMap(Entry::getKey, Entry::getValue));
}

-1

Chris J的答案的基础上,这里展示他的“hack”代码实现,证明它是可行的:

public static void main(String[] args) {
    Map<Object, Object> valueMap = new HashMap<Object, Object>();
    valueMap.put("foo", "bar");
    @SuppressWarnings("unchecked")
    Map<String, String> targetMap = (Map<String, String>) ((Object) valueMap); // hack alert!
    for (Map.Entry<String, String> entry : targetMap.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        System.out.println(key + "=" + value); // prints foo=bar :)
    }
}

哇!没有运行时异常 :)


1
可怕的、可怕的黑客。在生产代码中使用它的人,在我看来应该被开除并枪毙 ;) 请注意,我认为你只是提到了这个,因为它是一个巧妙的技巧,而不是建议使用它。但总是很有趣向人们展示一个 Map<String> 可以包含一个整数.. - Voo
好吧...我说过这是个不太正规的hack ;-) - Chris J
虽然我同意通常这是错误的,但在某些情况下,对象映射是适当的。 - Jay

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