遍历Map的Entry集合

19

我需要迭代一个参数类型未知的映射的条目集。

当迭代这样的entryset时,为什么会编译不通过?

public void myMethod(Map anyMap) {
    for(Entry entry : anyMap.entrySet()) {
        ...
    }
}

但是这个编译:

public void myMethod(Map anyMap) {
    Set<Entry> entries = anyMap.entrySet();
    for(Entry entry : entries) {
        ...
    }
}

而且这个也可以编译(但我不能使用它,因为我不知道地图的类型):

public void myMethod(Map<String, String> stringMap) {
    for(Entry<String,String> entry : stringMap.entrySet()) {
        ...
    }
}

检查你的变量名。 - Renjith
即使是你的第二个代码片段也不应该编译。变量名不同。你确定它编译了吗?发布编译后的代码,不要自己打。 - Rahul
谢谢@Renjith,已修正变量名称。 - Sergio
6个回答

24

你在第一个例子中遇到的错误是:

Type mismatch: cannot convert from element type Object to Map.Entry

这是因为编译器将您的FOR-IN循环转换为:

for (Entry entry : anyMap.entrySet()) {
}

致:

for (Iterator i = anyMap.entrySet().iterator(); i.hasNext();) {
    Entry e = i.next(); // not allowed
}
你的第二个例子能够工作,但是只有通过欺骗才行!你正在进行未经检查的强制类型转换,以将 Set 转换回一个 Set。

你的第二个例子能够工作,但是只有通过欺骗才行!你正在进行未经检查的强制类型转换,以将 Set 转换回一个 Set<Entry>

Set<Entry> entries = anyMap.entrySet(); // you get a compiler warning here
for (Entry entry : entries) {
}

成为:

Set<Entry> entries = anyMap.entrySet();
for (Iterator<Entry> i = entries.iterator(); i.hasNext(); ) {
    Entry e = (Entry) i.next(); // allowed
}

更新

如评论中所述,由于编译器的原始类型擦除规则,类型信息在两个示例中都丢失了。

为了提供向后兼容性,所有原始类型实例的方法都被其擦除的对应方法替换。 因此,因为您的Map是一个原始类型,它全部被擦除了。 包括其Set<Map.Entry<K,V>> entrySet(); 方法:强制使用擦除版本的原始类型实例:Set entrySet()


但是为什么在第一个案例中,'anyMap.entrySet().iterator()'没有返回'Iterator<Entry>'呢? 'entrySet()'的返回值是'Set<Map.Entry<K,V>>',因此即使未提供键和值类型,该集合的迭代器也应该使用'Entry'进行参数化,对吧? - Sergio
@Sergio,这可能是因为转换后的迭代器没有使用泛型。 - Navin Israni
@Sergio 我已经添加了一个更新。因为anyMap是原始的Map类型,所以它的entrySet方法的返回类型实际上只是Set。这导致Iterator只能从Objects中工作。 - David Lavender

8

由于您使用了原始类型Map,因此map.entrySet()会返回一个非参数化的Set,在迭代时返回Object而不是Entry。

一个简单但优雅的解决方案是使用Map<?,?>,这仍然允许您传递任何Map,但另一方面强制map.entrySet()的返回值为Set<Entry>:

public void test(Map<?,?> map) {        
    for(Entry e : map.entrySet()){
        Object key = e.getKey();
        Object value = e.getValue();
    }       
}

但是,如果'entrySet()'的返回值是'Set<Map.Entry<K,V>>',为什么即使未提供键和值类型,结果集也没有使用'Map.Entry'进行参数化呢? - Sergio
for( java.util.Map.Entry<?, ?> item : ( ( java.util.Map<?, ?> )map ).entrySet() ) {...} 正常工作。 - udoline

2

在第一个例子中,map.entrySet() 返回 Set,当你在循环中迭代时使用 Iterator。没有关于 set 内容类型的信息,所以 Java 使用 Object 作为基础类型,并告诉你它无法将 Object 转换为 Entry。

Set<Map.Entry<K, V>> entrySet();

当您使用原始的map类型时,entrySet只会返回一个没有类型信息的Set。
在第二个例子中,您手动将Set转换为带有警告的Set<Entry>。现在Java知道里面的内容。因此,您可以进行迭代。
在最后一个例子中,您知道map类型,因此entrySet返回正确的类型化Set,您可以在不需要任何类型约定的情况下进行迭代。

1

第一个代码片段无法编译,因为变量map不存在。你将参数称为anyMap,但尝试以map的形式访问它,修复这个问题,你的代码就可以编译了,除了一些原始类型警告。


@BlankChisui。假设地图名称是一个打字错误,问题仍然是OP所提到的。这不是名称错误的问题。 - Rohit Jain
哦,我的错。下次最好先测试一下我的假设。至少我找到了一个错误。 - Blank Chisui

0

你之所以会出现编译时错误,是因为你没有在 HashMap 中指定类型。

就像这样:

HashMap <(Integer,String> hm=new HashMap<(Integer,String>();  

如果您将类型指定为整数和字符串,则不会出现编译时错误。

如果您不知道要添加到哈希映射中的值,则可以使用以下代码:

HashMap<(Object,Object> hm=new HashMap();  

0

我曾经遇到过同样的问题。看起来强制转换是个问题。我尝试了下面的代码,它可以工作。

    for(Object entry : hashMap.entrySet())
    {
        System.out.println(((Entry<Object, Object>) entry).getKey() + " = " + ((Entry<Object, Object>) entry).getValue());

    }

不要一开始就使用原始类型,这样你就可以避免这种令人讨厌的代码。 - Tom
@Tom 但整个问题都是关于处理原始类型的。为什么要对一个随机答案进行指责呢? - Nils-o-mat
@Nils-o-mat 你知道2013和2017之间的区别吗? - Tom

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