Casting a HashMap - java

i´m trying to make a casting of Hasmap
I have this hasmap:
Map<String, Object> requestargs = new Map<String, Object>();
Other side i have a method who bring me a hashmap of: Map<String, Document>
MultipartForm form = MgnlContext.getWebContext().getPostedForm();
The method is getDocuments();
I need to put the return of this method in my hashmap making something like this:
requestargs = form.getDocuments();
But i don´t know how to cast this Hasmap og (String,Document) to (String,Object)
Thanks

Unless you can use a wildcard
Map<String, ? extends Object> requestargs
You will need to copy the map into a new map:
requestargs = new HashMap<String, Object>(form.getDocuments());
The two types are not related directly. Were you able to make the assignment directly (or via casting) it would be possible to insert a value with non-Document type into the map, and that would be type-unsafe:
Map<String, Document> docs = form.getDocuments();
Map<String, Object> requestargs = docs; // not actually allowed
requestargs.put("Foo", new Object());
for (Document doc : docs.values()) {
// doc isn't necessarily a Document! ClassCastExceptions abound.
}
To prevent this problem happening, such an assignment is forbidden by the type system.
The wildcard works because it makes it impossible to call put on the map, since there is no way to know what types can be put into the map safely.

Related

java - to set multiple value in a map

I got a scenario like the following:
Map1 - Map<String, Map<String,List<Vo>>>
Map2 - Map<String, Set<String>
Is it possible to set the same have a same key reference for the above 2 Maps like the following?
Map<String, Collection<?> mapCommon=new HashMap<String, Collection<?>();
Can anyone please give some idea about how to set this?
edit: yes same reference
You are touching here two interesting elements.
Firstly - Map does not belong to Collection. List and Set do belong, but Map is a different one even though it shares some commonalities with Lists and Sets.
Secondly - Mixing the types into one commonMap the way you are trying is doable but it should be avoided as it is generally not considered as best practice. The problem we are dealing with is caused by type erasure. Once compiler compiles the code - it does not pass any information about generic types hold by Map or Set. Effectively your Map<String, List<Vo>> becomes raw-type Map<?> in the compiled code. The problem with that is casting back original values. The compiler will not allow you to check the instance if it is Map<String, List<Vo>> or Set<String>.
The fllowing piece of code will fail:
public static void processElement(Object commonMapObjectEitherMapOrSet) {
if (commonMapObjectEitherMapOrSet instanceof Map<String, List<Vo>>) {
//...
}
}
Error: Cannot perform instanceof check against parameterized type
Map>. Use the form Map instead since further
generic type information will be erased at runtime
The possible workaround would be to forget about generics and check if the instance is a raw-type Set or Map. The code below shows how check if Object is either Map or Set.
public static void processElement(Object commonMapObjectEitherMapOrSet) {
if (commonMapObjectEitherMapOrSet instanceof Map) {
System.out.println("Got map; but types held in the map are not known due to type-erasure");
// This is where things will get messy as you will get warnings:
Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObjectEitherMapOrSet;
// ...
}
if (commonMapObjectEitherMapOrSet instanceof Set) {
System.out.println("Got set; but types held in the set are not known due to type-erasure");
// This is where things will get messy as you will get warnings:
Set<String> set = (Set<String>) commonMapObjectEitherMapOrSet;
// ...
}
}
The problem with the above is casting the value from your commonMap back to your desired types ie. Map<String, List<Vo>> and Set<String>. The compiler won't be able to check if the casting is correct and will issue a warning. You can technically Suppress the warning with (#SuppressWarnings("unchecked") annotation ) but this may not be the best thing to do.
At this stage - it makes sense to consider whether or not to create your own specialized class to manage different types.
Back to your original question - to answer it I am posting the code that maps things to the common map:
package stackoverflow;
import java.util.*;
class Vo {}
public class MultipleRefs {
public static void main(String[] args) {
Map<String, List<Vo>> mapVo = new HashMap<>();
Set<String> set = new HashSet<>();
Map<String, Object> commonMap = new HashMap<>();
//commonMap.put("a", Map)
commonMap.put("mapVoOne", mapVo);
commonMap.put("setOne", set);
commonMap.forEach((key, value) -> processElement(value));
}
public static void processElement(Object commonMapObject) {
if (commonMapObject instanceof Map) {
System.out.println("Got map; but types held in the map are not known due to type-erasure");
// This is where things will get messy:
Map<String, List<Vo>> map = (Map<String, List<Vo>>) commonMapObject;
System.out.println(" processElement prints map: " + map);
}
if (commonMapObject instanceof Set) {
System.out.println("Got set; but types held in the set are not known due to type-erasure");
// This is where things will get messy:
Set<String> set = (Set<String>) commonMapObject;
System.out.println(" processElement prints set: " + set);
}
}
}
If I understand you would want to have the same key to be used for various different types of values.
Why not have a new Class itself that would consists of maps, sets, whose instances could be used as values
class MyClass {
private Map<String, List<Vo>> theMap;
private Set<String> theSet;
...
... // have its own getters and setters
}
And then you can have your top level map defined like this
Map<String, MyClass> myMainMap = new HashMap<String, MyClass>();
Or as an alternative have a tuple
You can check this link further to see how that is done.
What you want to do is impossible because Set and Map do not share any common implementation or super class except Object. You can see it in the official documentation :
Javadoc Map
Javadoc Set
You could do a Map<String, Object> but I strongly not advise you to doing that. How could you know if your object is a map or a set ? It is not possible to do that properly.
In my opinion, the best solution you have is to create a new class to wrap your two collections :
public class YourWrapper {
Map<String, Map<String,List<Vo>>> a;
Map<String, Set<String> b;
// getter setter etc...
}
After that you can create your collection :
Map<String, YourWrapper> myMap = new HashMap<String, YourWrapper>();

Converting Map<String,String> to Map<String,Object>

I have Two Maps
Map<String, String> filterMap
Map<String, Object> filterMapObj
What I need is I would like to convert that Map<String, String> to Map<String, Object>.
Here I am using the code
if (filterMap != null) {
for (Entry<String, String> entry : filterMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Object objectVal = (Object)value;
filterMapObj.put(key, objectVal);
}
}
It works fine, Is there any other ways by which I can do this without iterating through all the entries in the Map.
Instead of writing your own loop that calls put, you can putAll, which does the same thing:
filterMapObj.putAll(filterMap);
(See the Javadoc.)
And as Asanka Siriwardena points out in his/her answer, if your plan is to populate filterMapObj immediately after creating it, then you can use the constructor that does that automatically:
filterMapObj = new HashMap<>(filterMap);
But to be clear, the above are more-or-less equivalent to iterating over the map's elements: it will make your code cleaner, but if your reason for not wanting to iterate over the elements is actually a performance concern (e.g., if your map is enormous), then it's not likely to help you. Another possibility is to write:
filterMapObj = Collections.<String, Object>unmodifiableMap(filterMap);
which creates an unmodifiable "view" of filterMap. That's more restrictive, of course, in that it won't let you modify filterMapObj and filterMap independently. (filterMapObj can't be modified, and any modifications to filterMap will affect filterMapObj as well.)
You can use the wildcard operator for this.
Define filterMapObj as Map<String, ? extends Object> filterMapObj and you can directly assign the filterMap to it. You can learn about generics wildcard operator
You can simply write
Map<String, Object> filterMapObj = new HashMap<>(filterMap);
You can use putAll method to solve the problem.The Object is the father class of all objects,so you can use putAll without convert.

Inheritance doesn't work with passed as generic type

Consider my custom extended hashmap:
public class CustomHashMap extends HashMap<String, Object> {
...
}
Why doesn't this work since CustomHashMap is child of HashMap?
Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
But this works:
Map<String, HashMap<String, Object>> customs = new LinkedHashMap();
And also it works when adding (put) an CustomHashMap into the customs Map.
customs.put("test", new CustomHashMap());
It seems weird that not specifying the generics at initialization works, but it doesn't otherwise.
This statement is not working
Map<String, HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
because customs is of type Map<String, HashMap<String, Object>> and you are assigning a LinkedHashMap which is of type <String, CustomHashMap>, where CustomHashMap is a sub class of HashMap<String, Object>.
Generics are invariant: for any two distinct types T1 and T2, HashMap<String, T1> is neither a subtype nor a supertype of HashMap<String, T2>. So, LinkedHashMap<String, CustomHashMap> cannot be assigned to Map<String, HashMap<String, Object>>. On the other hand, arrays are covariant, which means below statement will compile without any error or warning. But, it might fail at run time (which might cause more harm) if you put any other subtype of HashMap<String, Object> into it other than CustomHashMap :
HashMap<String, Object>[] mapArray = new CustomHashMap[1];
mapArray[0] = new CustomHashMap_1();// this will throw java.lang.ArrayStoreException
Now, if you want to assign LinkedHashMap<String, CustomHashMap> to Map<String, HashMap<String, Object>> , change the statement to this:
Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
Some additional information about this approach is nicely explained by #Seelenvirtuose , which is the accepted answer.
When working with generics, you should always keep type erasure in mind. At runtime an objct of type Map does not know its type parameters anymore. The consequence: A LinkedHashMap<String, CustomHashMap> is not a sub-type of Map<String, HashMap<String, Object>>.
If you want to have somthing sub-type related you must do it the following way:
Map<String, ? extends HashMap<String, Object>> customs = new LinkedHashMap<String, CustomHashMap>();
This is called an upper-bounded wildcard and exists exactly for that case: To get a sub-type relationship. Please refer to the Java tutorial about generics for more information.
An additional info as per the comment:
The upper-bounded version has a disadvantage on how to use the customs map. You cannot put instances anymore into that map. The only value allowed is null. The reason is, that you could have another class extending Map<String, HashMap> and try to put an instance of that into your customs map. But this is a problem, as the variable customs refers to a map that was parameterized with CustomHashMap.
When working with bounded wildcards, you should always remind PECS. PECS stands for "producer extends, consumer super". This is valuable for method parameters. If you write a method that only needs to read values from such a map, you could type the parameter as Map<String, ? extends Map<String, Object>>. This is called a producer. If you only need to write to that map, use the keyword super. If you need both - read and write - you cannot do either.
From the java tutorial on oracle's site
List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2
Line 1 is certainly legal. The trickier part of the question is line 2. This boils down to the question: is a List of String a List of Object. Most people instinctively answer, "Sure!"
Well, take a look at the next few lines:
lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!
Here we've aliased ls and lo. Accessing ls, a list of String, through the alias lo, we can insert arbitrary objects into it. As a result ls does not hold just Strings anymore, and when we try and get something out of it, we get a rude surprise.
The Java compiler will prevent this from happening of course. Line 2 will cause a compile time error.
this link would help you to learn generics and subtyping

Why can you cast Maps of different types?

I came across an oddity today that I don't quite understand. Take this code, for example:
Object iMap = new HashMap<Integer, Object>() {{
put(5, "thing1");
put(6, "thing2");
}};
Map<String, Object> sMap = (Map<String, Object>)iMap;
// No error, prints out java.lang.Integer:
System.out.println(new ArrayList(sMap.keySet()).get(0).getClass().getName();
// No error, prints out 5:
Object key = new ArrayList<String>(sMap.keySet()).get(0);
System.out.println(key.toString());
// ClassCastException:
String s = new ArrayList<String>(sMap.keySet()).get(0);
So, what gives? Why can I cast a Map with keys of type Integer to one of type String without any issues? Shouldn't that throw an error? And why can I even cast to ArrayList<String> and still get no errors? It's supposedly a list of only Strings, but I can retrieve an Integer from it.
I'm a bit baffled by this, and I'm wondering if anyone here knows enough about the inner workings of these classes to help me out.
You can cast Map<Integer, Object> to Map<String, Object> "without any issues"... until you try to use it.
The problem starts in this line:
Map<String, Object> sMap = (Map<String, Object>)iMap;
where the compiler warns you with this message:
Type safety: Unchecked cast from Object to Map
You ignored that warning.
This is all happening because of runtime type erasure - at runtime there are no types, eg you have just Map, etc. Types are just there at compile to help you not do what you are doing here.
The reason this line explodes:
String s = new ArrayList<String>(sMap.keySet()).get(0);
is that the sMap actually refers to the Map that had Integers for keys in its entries. When you actually went to pull one of the keys out, it was an Integer which java then tries to assign to a String... boom!
btw, this part doesn't compile:
Object iMap = new HashMap<Integer, Object>();
iMap.put(5, "thing1");
iMap.put(6, "thing2");
you would need to cast iMap to Map<Integer, Object> like this:
Object iMap = new HashMap<Integer, Object>();
((Map<Integer, Object>)iMap).put(5, "thing1");
((Map<Integer, Object>)iMap).put(6, "thing2");
The first four lines of your code won't compile as iMap must be declared at least as a Map in order to call methods such as get(...) on it. But your other problem illustrates why it is important to use generics when declaring variables. So if you declared iMap thusly:
Map<Integer, Object> iMap = new HashMap<Integer, Object>();
The compiler would complain rightly when you try to cast it here:
Map<String, Object> sMap = (Map<String, Object>)iMap;

What is the difference between ? and Object in Java generics?

I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.

Categories