19

I need to iterate over the entry set of a map from which I do not know its parameterized types.

When iterating over such entryset, why this does not compile ?

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

but this compile:

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

and this also compiles (I cannot use this one since I do not know the types of the map):

public void myMethod(Map<String, String> stringMap) {
    for(Entry<String,String> entry : stringMap.entrySet()) {
        ...
    }
}
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Sergio
  • 8,532
  • 11
  • 52
  • 94

6 Answers6

24

The error you get in your first one is:

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

This is because the compiler converts your FOR-IN loop:

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

To:

for (Iterator i = anyMap.entrySet().iterator(); i.hasNext();) {
    Entry e = i.next(); // not allowed
}

Your second example works, but only through cheating! You are doing an unchecked cast to get Set back into a Set<Entry>.

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

Becomes:

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

Update

As mentioned in comments, the type information is getting lost in both examples: because of the compiler's raw type erasure rules.

To provide backwards compatibility, ALL methods of raw type instances are replaced by their erased counterparts. So, because your Map is a raw type, it all gets erased. Including its Set<Map.Entry<K, V>> entrySet(); method: your raw type instance will be forced to use the erased version: Set entrySet().

David Lavender
  • 8,021
  • 3
  • 35
  • 55
  • but then why in the first case 'anyMap.entrySet().iterator()' is not returning an 'Iterator' ?. The return value of 'entrySet()' is 'Set>', so an iterator of this set should be parameterized with 'Entry' even if the Key and Value types are not provided, right ? – Sergio Feb 18 '13 at 11:05
  • @Sergio this is probably because the converted Iterator is not using Generics. – Navin Israni Nov 20 '13 at 10:14
  • @Sergio I've added an update. Because `anyMap` is a raw `Map` type, the return type of its `entrySet` method is actually just `Set`. Which then causes the `Iterator` to just be working from Objects. – David Lavender Jun 16 '15 at 15:07
8

It is because you use the raw type Map, therefore map.entrySet() gets you a non-parametrized Set which in return yields Object on iteration, not Entry.

A simple, but elegant solution is to use Map<?,?>, which will still allow you to pass ANY Map, but on the other hand forces map.entrySet() to have a return value of Set<Entry>:

public void test(Map<?,?> map) {        
    for(Entry e : map.entrySet()){
        Object key = e.getKey();
        Object value = e.getValue();
    }       
}
Polygnome
  • 7,639
  • 2
  • 37
  • 57
  • but if the return value of 'entrySet()' is 'Set>, why the resulting set is not parameterized with 'Map.Entry' even if the Key and Value types are not provided ? – Sergio Feb 18 '13 at 11:17
  • for( java.util.Map.Entry, ?> item : ( ( java.util.Map, ?> )map ).entrySet() ) {...} works properly – udoline May 14 '19 at 14:13
2

In first example, map.entrySet() returns Set, when you iterate in loop you use Iterator. There is no information about set content type, so Java use Object as Object is base type and compiler tells you that it can't convert Object to Entry.

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

When you use raw map type, entrySet returns just Set, with no type information.

In second example, you manualy convert Set to Set<Entry> (with warning). Now Java knows what inside it. So you can iterate.

In last example you know map type, so entrySet returns correct typed Set and you can iterate without any type conventions.

Leonidos
  • 10,482
  • 2
  • 28
  • 37
1

the first code snippet wont compile because the variable map does not exist. You called the parameter anyMap but tried to access it as map, fix that and your code will compile aside from some rawtypes warnings

Blank Chisui
  • 1,043
  • 10
  • 25
  • @BlankChisui. Assuming that the name of the map was a typo. The problem will still be what OP has mentioned. This is not the issue of wrong name. – Rohit Jain Feb 18 '13 at 10:23
  • Oh, my bad. Better test my assumtions beforehand next time. At least I got one error. – Blank Chisui Feb 18 '13 at 11:29
0

I have faced the same problem. It seems that the casting is a problem. I tried the below code and it worked.

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

    }
0

You're getting the compile time rrror because you are not specifying the type in HashMap

Like

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

If you specify the type as Integer and String, you don't get the compile time error.

IF you Dont Know which Value is going to Add in your HashMap then You Use

HashMap<(Object,Object> hm=new HashMap();  
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
Ziyad
  • 1
  • 3