when I declare a Map or Map <Object,Object> I can put anything in this Map
Map map = new HashMap();
map.put("");
but if I declare it as Map <?,?> I can put nothing in it
Map<?,?> map = new HashMap();
map.put("");
it will goes wrong why ?
Map<?,?> map = new HashMap<Integer, Integer>(); // compiles just fine!
? represents some fixed but unknown type. You can't put "" in a Map<Integer, Integer>, and a Map<?, ?> is allowed to be any type of Map, including a Map<Integer, Integer>.
In java collection Frame, ? means unknown type. You can only read elements from that, but can not add elements except for NULL value.
So you can compile fine like below:
Map<?,?> map = new HashMap();
map.put(null, null);
For this snippet code:
Map<?,?> map = new HashMap();
Map<?,?> means a Map typed to an unknown type.
The question mark (?), called the wildcard,the wildcard means "the value type parameter could be anything", it doesn't mean "you can use this as if it were anything you want it to be".
For more info go to link
Related
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 need to convert raw Map to Map<string,string>, and I think I have to first convert the raw map to Map<Object,Object> and then convert it again to Map<String,String>.
code snippet goes like below.
Map obj1 = new HashMap();
obj1.put("key1", 1);
obj1.put("key2", false);
obj1.put("key3", 3.94f);
Map<Object, Object> obj2 = obj1;
Map<String, String> obj = new HashMap<String,String>();
for (Map.Entry<Object, Object> entry: obj2.entrySet()) {
obj.put(entry.getKey().toString(), entry.getValue().toString());
}
I guess it would work in any condition but I want to hear from others about possible danger of this code.(any possiblities for ClassCastException for example?)
Please also let me know if you have a better idea.
-- revised code
Map obj1 = new HashMap();
obj1.put(2, 1);
obj1.put(true, false);
obj1.put(4.4f, 3.94f);
Map<String, String> obj = new HashMap<String,String>();
for (Object k : obj1.keySet()){
obj.put(k.toString(), obj1.get(k).toString());
}
Since raw Map entries will contain key/value of Objects anyway, I think I don't need temporary Map<Object,Object>. Just iterating over each item works well and I don't see any issues so far.
If You Look out the Definition of HashMap in jdk 1.4 It was earlier Implements using Object Class when generics Concept not came.
When generics is Introduced this object is Replaced with <T>. But If you Still don't use Generics Type Safe then Internally this Statement new HashMap() reflects a instance of <Object, Object>. Better To use directly a
a new HashMap() is better idea. There should no need of Map <Object, Object> obj2.
So, GO For this.. a better approach.
Map obj1 = new HashMap();
obj1.put("key1", 1);
obj1.put("key2", false);
obj1.put("key3", 3.94f);
Map<Object, Object> obj2 = obj1;
Map<String, String> obj = new HashMap<String,String>();
for (Object obj_Entry : obj1.entrySet()) {
Map.Entry entry = (Map.Entry) obj_Entry; // This will Work Fine all Time.
obj.put(entry.getKey().toString(), entry.getValue().toString());
}
Your code will not generate ClassCastExceptions. Actually you are not doing any casting here. You just call the toString() method of every key/value pair to make it a string. As long as toString() returns a valid value of your objects. Your code will be fine.
But your code may produce NullPointerExceptions if your obj1 contain null keys or objects
obj1.put(null, "null value")
Also note that some key collisions may occur if toString() methods return same String value for two keys. This is unlikely but it is possible.
I would like to instantiate Map<String, List<String>> in Java,
I tried
Map<String, List<String>> foo = new <String, List<String>>();
and
Map<String, List<String>> foo = new <String, ArrayList<String>>();
None of them work. Does any one know how to instantiate this map in Java?
new HashMap<String, List<String>>();
or as gparyani commented:
new HashMap<>(); // type inference
Note: each entry needs to be given an instantiated List as a value. You cannot get("myKey").add("some_string_for_this_key"); the very first time you get() a List from it.
So, fetch a List, check if it's null.
If it's null, make a new list, add the string to it, put the List back.
If it's anything but null, add to it, or do what you want.
You forgot to mention the class. Map here is the reference type and is an Interface. HashMap on the other side of equals specifies the actual type of the Object created and assigned to the reference foo.
Map<String, List<String>> foo = new HashMap<String, List<String>>();
The actual type specified (HashMap here) must be assignable to the reference type (Map here) i.e. if the type of reference is an Interface, the Object's type must implement it. And, if the type of the reference is a Class, the Object's type must either be the same class or its subtype i.e. it extends from it.
From Java 7 onwards, you can use a shorthand like
Map<String, List<String>> foo = new HashMap<>();
Your second way of instantiation is not recommended. Stick to using List which is an Interface.
// Don't bind your Map to ArrayList
new TreeMap<String, ArrayList<String>>();
// Use List interface type instead
new TreeMap<String, List<String>>();
Map is an interface. You have to tell Java which concrete Map class you want to instantiate.
Map<String, List<String>> foo = new HashMap<String, List<String>>();
or
Map<String, List<String>> foo = new TreeMap<String, List<String>>();
etc.
I have a map like the one below
final Map<String, ? extends Object> map
Can anyone tell me why this operation is not possible..?
productMap.put("min", String.valueof(34));
What should be the turnaround...
You can't add any object to a Map because the compiler knows the value is some class which extends Object, but doesn't know which one.
Map<String, ? extends Object> map = new HashMap<String, Integer>();
Object val = map.get("min"); // this is ok.
map.put("min", Integer.toString(34)); // not allowed.
Instead you can use
Map<String, Object> map = new HashMap<String, Object>();
Object val = map.get("min"); // this is ok.
map.put("min", Integer.toString(34)); // is ok.
The wildcard provides flexibility (you can now assign a HashMap<String, String> or a HashMap<String, Integer> to map) in exchange for a condition: you cannot write to map, because it doesn't know what the actual class of the values will be.
See here for a good tutorial.
You've told the compiler that the map values will be some specific subtype of Object. ? could be anything -- you could do:
Map<String,? extends Object> map = new HashMap<String,Integer>();
So String might be invalid.
You probably want the simpler Map which does allow any value.
Or you can "cheat" and do a cast which hides the generic type:
((Map)productMap).put("min", String.valueof(34));
But that trick is not best practice & to be used sparingly if at all.
By declaring the map as Map<String, ? extends Object>, you state that the second type is "anything which inherits from object". This is equivalent to declaring the map as Map<String, ?>. This declaration enables assignments like
final Map<String, ? extends Object> map = new HashMap<String, Integer>();
I think this example makes it clear why the compiler forbids inserting strings into the map: The value type is unspecified in the declaration.
Why isn't a Map<String,List<SomeBean>> castable to Map<String,List<?>>?
What I'm doing now is this:
Map<String, List<SomeBean>> fromMap = new LinkedHashMap<String, List<SomeBean>>();
/* filling in data to fromMap here */
Map<String,List<?>> toMap = new LinkedHashMap<String, List<?>>();
for (String key : fromMap.keySet()) {
toMap.put(key, fromMap.get(key));
}
In my opinion there should be a way around this manual transformation, but I can't figure out how. Any Ideas?
The cast is invalid because in Map<String,List<?>> you can put List<String> and List<WhatEver>, but not in Map<String, List<SomeBean>>.
For instance:
//List<SomeBean> are ok in both lists
fromMap.put("key", new ArrayList<SomeBean>());
toMap.put("key", new ArrayList<SomeBean>());
//List<String> are ok in Map<String,List<?>>, not in Map<String, List<SomeBean>>
fromMap.put("key", new ArrayList<String>()); //DOES NOT COMPILE
toMap.put("key", new ArrayList<String>());
To simplify your code, you may use the appropriate constructor to simplify your code:
Map<String, List<SomeBean>> fromMap = new LinkedHashMap<String, List<SomeBean>>();
Map<String,List<?>> toMap = new LinkedHashMap<String, List<?>>(fromMap);
Not realy an answer to your question, but as an extra: I would not use keyset here... If you want to iterate through all the elements of a map, use the entrySet() method. Its faster because it does not require the key-value lookup for each element.
for (Map.Entry<String, List<SomeBean>> entry : fromMap.entrySet()) {
toMap.put(entry.getKey(), entry.getValue());
}
If you really want to, you could cast to a raw Map (but what you want is not type safe):
Map<String,List<?>> toMap = (Map) new LinkedHashMap<String, List<String>>();
When assigning to a Map, where K and V are not wildcard parameters, the Map being assigned must have exactly the same K and V. In your case, V must be exactly List<?>.
The workaround it to use a wildcard V.
Map<String, ? extends List<?>> map = new LinkedHashMap<String, List<String>>();
Because the V you are assigning to is a wildcard, the V being assigned must only be assignable to V (rather than being exactly V).