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).
Related
Can I typecast a complex structure like HashMap<String,List<HashMap<String,String>>> to Map<String,List<Map<String,String>>> ?
I would recommend stream the outer and inner map and then assign to parent reference Map
Map<String,List<hMap<String,String>>> res = map.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
entry->entry.getValue().stream().map(HashMap::new).collect(Collectors.toList())));
A List<HashMap<String,String>> is not a List<Map<String,String>>. You can pretend and use the raw type at your own risk:
HashMap<String, List<HashMap<String,String>>> map = new HashMap<>();
Map<String,List<Map<String,String>>> cast = (Map) map;
You'll get a warning for unchecked assignment, because you can then do this:
String key = "";
cast.put(key, Collections.singletonList(new TreeMap<>())); // Not a HashMap
HashMap<String, String> inserted = map.get(key).get(0);
This compiles with no warning but will throw a ClassCastException.
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);
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
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 am trying to create a dictionnary in a <K, List<V>> format.
private static Map<String, Collection<String>> dict = new HashMap<String, Collection<String>>();
Using new HashMap<>(); or new HashMap<String, ArrayList<String>>(); throws incompatible data types error
I need a dictionary similar to the one below.
a: apple, ajar, axe, azure
b: ball, bat, box
d: dam, door, dish, drown, deer, dare
u: urn, umbrella
y: yolk
To do this, I worte below code. put() returns incompatible paramters compilation error. What is the right way to use put() for this example?
dict.put("a", "apple");
dict.put("a", "ajar");
.
.
.
dict.put("u", "umbrella");
dict.put("y", "yolk");
You need to place a List as the value to the map, for example:
List<String> listA = Arrays.asList("apple", "ajar", "axe", "azure");
dict.put("a", listA);
Alternatively, you can use guava Multimap which allows more than one value to be mapped to a given key.
This is because you need to put an arrayList in the value as your Map declaration is Map<String, Collection<String>> so it cannot take Map<String, String>.
ArrayList<String> list = new ArrayList<String>();
list.add("apple");
dict.put("a",list );
As per java 7 you can do it using diamond operator so you can create a map as,
List<String, List<String>> = new ArrayList<>();
What you need is this;
List al = new ArrayList<String>();
al.add("apple");
al.add("ajar");
HashMap<String, List<String>> hm = new HashMap<String, List<String>>();
hm.put("a", al);
System.out.println(hm.get("a"));
This is because, when you use;
private static Map<String, Collection<String>>
You need a Collection like a List. DoNOT insert Objects as Strings
You can only follow the definition you have done :
Map<String, Collection<String>> implies you use dict.put(a,b) with a being a String and b a Collection.
You're trying to put a String as a value that's your problem.
You may want to do something like that :
Collection col = dict.get("a");
if (col == null) {
col = new ArrayList();
}
col.add("apple");
dict.put("a",col);
I would first change the type of the dictionary to
private static Map<Character, ArrayList<String>> dict = new HashMap<>();
It'll allow easier putting of array lists as generics are not covariant.
For each letter, create:
ArrayList<String> myList=new ArrayList<>();
and put() it to dict with
dict.put(myList);
Then you can add words with:
dict.get(letter).put(word);
Your exact need is MultiMap feature of apache-commons
MultiMap dict = new MultiHashMap();
dict.put("a", "apple");
dict.put("a", "ajar");
.
.
.
dict.put("u", "umbrella");
dict.put("y", "yolk");