This works ok:
Map aMap;
aMap = new HashMap<String, TreeSet<String>>();
This does not compile:
Map<String, Set<String>> aMap;
aMap = new HashMap<String, TreeSet<String>>();
Error message:
Compilation failed (26/05/2014 11:45:43) Error: line 2 - incompatible types -
found java.util.HashMap<java.lang.String,java.util.TreeSet<java.lang.String>>
but expected java.util.Map<java.lang.String,java.util.Set<java.lang.String>>
Why?
The first one works because you use a raw type (without generic) so you can put any type of map in there.
The second one doesn't work because a XXX<Set> is not a XXX<TreeSet>.
So you need to choose between:
Map<String, Set<String>> aMap = new HashMap<String, Set<String>>();
//or
Map<String, TreeSet<String>> aMap = new HashMap<String, TreeSet<String>>();
And in both case you will be able to write:
aMap.put("abc", new TreeSet<>());
The main difference is when you get an item from the map, with the former construct you won't have access to the TreeSet specific methods.
Finally, with Java 7+ you can omit the generic information on the right hand side and the compiler will determine it automatically for you:
Map<String, Set<String>> aMap = new HashMap<>();
Map<String, TreeSet<String>> aMap = new HashMap<>();
Use this instead:
Map<String, ? extends Set<String>> aMap;
aMap = new HashMap<String, TreeSet<String>>();
Because the Set's generic must not be the same than TreeSet's generic.
+1 to Peter's answer, TreeSet implements SortedSet which extends Set.
Map<String, ? extends Set<String>> aMap;
aMap = new HashMap<String, TreeSet<String>>();
will work fine.
Related
Im storing 2 map with different structure in single map like below,
Map<String, List<String>> colMap = new HashMap<String, List<String>>();
Map<String, String> appMap = new HashMap<String, String>();
// colMap assigning some values
// appMap assigning some values
Map<String, Map> mainMap = new HashMap<String, Map>();
mainMap.put("appMap", appMap);
mainMap.put("colMap", colMap);
I want to get map one by one and iterate the map.
If I try get map like below, getting error,
.......
Map colMap = map.get("colMap");
for(Entry<String, List<String>> entry : colMap.entrySet())
Error: Type mismatch: cannot convert from element type Object to Map.Entry<String,List<String>>
Why not just create a simple container POJO class (or record in Java 16+) for the two maps instead of mainMap and keep the relevant type-safety which to do it Java-way?
public class MapPojo {
private final Map<String, List<String>> colMap;
private final Map<String, String> appMap;
public MapPojo(Map<String, List<String>> colMap, Map<String, String> appMap) {
this.colMap = colMap;
this.appMap = appMap;
}
// getters, etc.
}
MapPojo mainMap = new MapPojo(colMap, appMap);
Error you are getting because when you are doing map.get operation your reference is Just Map without any Generics which will treated as Object class's reference. You should use generics like below and it will work -
Map<String, List<String>> colMap = map.get("colMap");
for(Entry<String, List<String>> entry : colMap.entrySet())
Below is my code snippet
Map<Object, Object> gobalMap = new HashMap<Object, Object>();
Map<String, Map<String, Integer>> mp = new HashMap<String, Map<String, Integer>>();
gobalMap.put("mp",mp );
((Map<String, Map<String, Integer>>)gobalMap.get("mp")).put("A", new HashMap<String, Integer>().put("A", 1));
error:
The method put(String, Map<String,Integer>) in the type Map<String,Map<String,Integer>> is not applicable for the arguments (String, Integer)
May I know where am doing wrong ..?
new HashMap<String, Integer>().put("A", 1)
This returns an Integer. But you want to add this to an object which stores Maps and not Integer. So that's not possible. Also as Thomas explained in the comments, your code would not work even if it compiled because put returns the previous value of the map so you will receive a NullPointerException.
I would recommend restructuring your code to make it more readable and to also make it work:
Map<Object, Object> gobalMap = new HashMap<Object, Object>();
Map<String, Map<String, Integer>> mp = new HashMap<String, Map<String, Integer>>();
gobalMap.put("mp",mp );
HashMap<String, Integer> aMap = new HashMap<>();
aMap.put("A", 1);
((Map<String, Map<String, Integer>>)gobalMap.get("mp")).put("A", aMap);
As others have already stated new HashMap<String, Integer>().put("A", 1) returns an Integer (the previously mapped value for key "A" so null in this case) and that is not a suitable value for a Map<String, Map<String, Integer>>.
You're creating a suitable map but don't actually put it into the map so the reference to that map is lost.
Since you're probably trying to only create a nested map if it doesn't exist already try this:
((Map<String, Map<String, Integer>>)gobalMap.get("mp"))
.computeIfAbsent( "A", k -> new HashMap<String, Integer>())
.put("A", 1);
This does the following:
get and cast the map from globalMap (if you'd not be sure this can't return null you could use computeIfAbsent() here as well)
get the nested map for key "A" and if it doesn't exist create a new one, add and return it
put the value 1 for key "A" into the nested map
new HashMap<String, Integer>().put("A", 1) returns an integer, because when you put into a hashmap, you get back the previous value held by that key. As such it cannot be the value in a Map<String,Map<String,Integer>>.
Perhaps you meant to cast gobalMap to a Map<String,Map<String,Integer>>. But you are actually casting gobalMap.get("mp") to a Map<String, Map<String, Integer>>.
This, on the other hand, would compile:
((Map<String, Integer>) gobalMap.get("mp")).put("A", new HashMap<String, Integer>().put("A", 1));
though I'm not sure it does anything useful.
you missed the bracket. correct code will be:
((Map<String, Map<String, Integer>>)gobalMap.get("mp")).put("A", new HashMap<String, Integer>()).put("A", 1);
I have an Object which is of type Map<String, String> which has few entries. I expected to get a ClassCastException while casting this object to Map<String, Integer>. But the cast was successful. Why is it that this did not throw any exception?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "value1");
map1.put("key2", "value2");
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Edit: Casting from o not map1.
Generic-checking is made at compile time,while casting checking is done at the time of running the program.So You had got casting exception at run time.
You parse it as Integer.parseInt(String) and put the value into map2.
Are you sure that's right?
Your example fails to compile:
Error:(21, 60) java: incompatible types: java.util.Map<java.lang.String,java.lang.String> cannot be converted to java.util.Map<java.lang.String,java.lang.Integer>
However, changing from map1 to o, does compile:
//...
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Perhaps you're looking for something like this?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "1");
map1.put("key2", "2");
Map<String, Integer> map2 = new HashMap<>();
map1.forEach((key,value) -> map2.put(key, Integer.parseInt(value)));
Why does this cast work?
import java.util.HashMap;
import java.util.Map;
public class TestMap {
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap = new HashMap<>();
Map<String, Object> aMap = new HashMap<String, Object>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String, Integer>>>>) aMap.get("key");
System.out.println(resultMap);
}
}
also this:
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
...
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
and so on...
How does this happen that the hidden map which is Map<String, Integer> gets successfully cast to Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap?
Always prints:
{fortytwo=42}
Also this works (Map instead of Map):
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
Map<String, Map> aMap = new HashMap<String, Map>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
System.out.println(resultMap);
}
EDIT: So as #shizhz says, it is because of Type Erasure of course! So the code above is equivalent to:
Map resultMap = new HashMap();
Map aMap = new HashMap();
Map hiddenMap = new HashMap();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map) aMap.get("key");
Which also works
Because java generics is used at compile time to provide tighter type checks, the type parameter is erased by compiler according Type Erasure rules:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
In code Map<String, Map> aMap = new HashMap<String, Map>();, the value in aMap is a raw type Map, which means the compiler has no idea what's the type it contains, when you try to cast a raw type of Map to any generics type of Map like Map<String, Integer>, the best compiler can do is giving you a warning. The generic type is erased at compile time and type cast will be generated when you get value from a generic map, so you can only get a runtime ClassCastException exception if the type mismatchs.
Let's have a look at the following example:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map<String, Integer> m = (Map<String, Integer>) map;
System.out.println(m);
Integer i = m.get("hello");// ClassCastException happens at here at runtime
}
I'm trying to convert a Map containing all kinds of keys and values to Map<String, Integer> but there's no compile error, after type erasure, the above code is actually equivalent to:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map m = (Map) map;
System.out.println(m);
Integer i = (Integer)m.get("hello");
}
Now you can easily tell why the last line caused ClassCastException.
Since you've declared aMap as Map<String, Object>, the compiler cannot tell if the values won't indeed be of type Map<String, Map<String, Map<String,Integer>>>. It will just give you an "Unchecked cast" warning to let you think about the consequences.
The cast works unless you're actually trying to do something with the values:
resultMap.get("fortytwo").isEmpty();
will result in
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.util.Map
If you had declared aMap as Map<String, Map<String, Map<String, Map<String, Map<String, Integer>>>>> you wouldn't be able to put hiddenMap in it in the first place.
Line 1 : Set<Object> singletons = new HashSet<Object>();
Line 2 : singletons.add(new Integer(2));
Line 3 : Map<String, Object> myMap = new HashMap<String, String>();
Why compiler complains at line 3 and doesn't complains at line 2?
As mentioned in the comments, if you change your first line to
Set<Object> singletons = new HashSet<String>();
you'll get the same error. This is because HashSet<String> (and even Set<String>) is not a subtype of Set<Object>. To see why, think about the operations you'll call on that collection. You've already made one call to add an Integer object to it.
singletons.add(new Integer(2));
That works fine because you instantiated singletons to be HashSet<Object>. If you had instantiated it as a HashSet<String>, then you'd be adding an Integer object to a String collection, which is not allowed.
Try either
Map<String, String> myMap = new HashMap<String, String>();
or
Map<String, Object> myMap = new HashMap<String, Object>();
but I would use the diamond operator on the right, if you use java version >= 7
Map<String, String> myMap = new HashMap<>();