Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with entry set of java.util.Map

I'm having a strange problem with the following code works.

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

for(Entry<String, Object> entry : map.entrySet()) {
   // 
}

while the code below does not compile.

Map map = new HashMap();

for(Entry entry : map.entrySet()) {  // compile error here
   // 
}

Any clues?

like image 269
fastcodejava Avatar asked Aug 28 '11 03:08

fastcodejava


People also ask

What is map entry set in Java?

entrySet() method in Java is used to create a set out of the same elements contained in the map. It basically returns a set view of the map or we can create a new set and store the map elements into them.

Why entry interface is used in map?

Entry interface enables you to work with a map entry. The entrySet( ) method declared by the Map interface returns a Set containing the map entries. Each of these set elements is a Map. Entry object.

Can a map entry be null?

Map doesn't allow duplicate keys, but it allows duplicate values. HashMap and LinkedHashMap allows null keys and null values but TreeMap doesn't allow any null key or value. Map can't be traversed so you need to convert it into Set using keySet() or entrySet() method.


2 Answers

The entrySet method signature is Set<Map.Entry<K, V>> entrySet() so you can only refer to Map.Entry if you've declared the generic types in the declaration like you did in the first example. In the second you're using raw types, so it's essentially Set<Object> entrySet() and you'd need a cast for it to work, e.g.

final Map map = new HashMap();

for(final Entry entry : (Set<Entry>)map.entrySet()) {
   // 
}
like image 140
Burt Beckwith Avatar answered Oct 16 '22 14:10

Burt Beckwith


Burt has the right reason and Henning expands on it in the comments. When referencing a member of a raw type, generics don't come into play at all, even generics that don't rely on the type parameter.

As an example, this should compile just fine...

public class DataHolder<T> {
    public List<T> ts;
    public List<String> strings = new ArrayList<String>();
}

//...
DataHolder holder = new DataHolder();
holder.strings.add(Integer.valueOf(42));

...even though T doesn't need to be mapped to a concrete type to know what the type of strings should be.

This is true for generic member methods as well, which is what you are running into. entrySet returns the raw type Set, not Set<Entry>, even though the type parameters would not need to be known to return a Set<Entry>. The behaviour is documented in the Java Language Specification, section 4.8:

The type of a constructor (§8.8), instance method (§8.8, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.

It's a very "gotcha" rule.

See also

Java Class Generics and Method Generics conflicts

like image 37
Mark Peters Avatar answered Oct 16 '22 14:10

Mark Peters