Integer object in a <String, String> hashmap - java

Create a (String, Object) hashMap and put values of different types.
Serialize the hashmap into a json string and convert back into a hashmap of (String, String)
When I try to get the value of key "key2", it will throw an exception saying "Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String".
Is there a good way to explain this? Does it mean even though I have a (String, String) map, the value in it is not necessary String?
Apologize for any confusion. Let me know if something is not clear.
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", 99);
map.put("key3", new Date());
JsonUtil jsonUtil = new JsonUtil();
String s = jsonUtil.toJson(map);
HashMap<String, String> newMap = jsonUtil.fromJson(s, HashMap.class);
String value = newMap.get("key2");

In map, the value of key "key2" is 99, which is an integer. The exception is possibly being thrown in the second to last line, not the last line. I'm not entirely sure why you are serializing and then immediately deserializing, but if you just want 99 as a String like "99", the way to do this would be:
String value = String.valueOf(map.get("key2"));
The serializing and deserializing would not be necessary in this case.
Better yet, if you just want a Map<String, String> for the whole time, you can do something like this:
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", String.valueOf(99));
map.put(new Date().toString());

When you call fromJson(s, HashMap.class), you're not providing any generic type information to the decoder--all it sees is HashMap, and the implicit return type from that call is HashMap<?,?>. JSON can assume string keys, and the decoder is doing its best to decode objects into their "native" representations, so when you tell the compiler that all of the keys and values are strings, there's a mismatch.
If you specify which JsonUtil is involved, we may be able to provide a more specific approach; otherwise, you could use something like a stream transform to convert every value to v.toString().

String value = newMap.get("key2");
In this line you are getting the value of key2 in hashMap and then assigning it to a String type variable value which is invalid. Because the value of key2 is Integer type, you cannot do this directly. You can put this value in `Integer type variable. as bellow:
int value = newMap.get("key2");
Or if you want a generic solution so that you can assign any type of variable into value then you can declare it as Object type. as bellow:
Object value = newMap.get("key2");

Related

Storing multiple datatypes in a single HashMap

I want to put an array of int and a String into a HashMap. Is this possible? what is the proper way to that?
This is my HashMap:
Map<String, String> stringMap = new HashMap<>();
stringMap.put("Text","fish" );
stringMap.put("Diet","false");
stringMap.put("CookingTime", "60");
stringMap.put("OptionId", [7,8,8]);
I know this line is wrong - how do I store an array in a HashMap?
stringMap.put("OptionId", [7,8,8]);
You can instantiate an array in java by doing
someMap.put("OptionId", new int[]{7,8,8});
Otherwise you will need a function that returns those values in an array.
For your case: if you want to create a HashMap of multiple datatypes you can use
HashMap<String, Object> map = new HashMap<String, Object>();
You can then put anything you want in
map.put("key1", "A String");
map.put("key2", new int[]{7,8,8});
map.put("key3", 123);
Except now the tricky part is you don't know what is what so you need to use instanceof to parse the map unless you know what type of object is at a key then you can just cast it to that type.
if(map.get("key1") instanceof String)
String s = (String) map.get("key1"); // s = "A String"
or
int[] arr = (int[]) map.get("key2"); // arr = {7,8,8}

Java: Type Safety warning with unchecked conversion

I am using java in my project. When I try to get keys from a json object I get this warning: Type safety: The expression of type Iterator needs unchecked conversion to conform to Iterator I tried to fix by #SupressWarning('unchecked') but eclipse still shows the warning on file. I don't know what is going wrong.
public static HashMap<String, String> jsonToMap(final JSONObject jsonMap)
throws JSONException {
HashMap<String, String> map = new HashMap<String, String>();
Iterator<Object> keys = jsonMap.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
String value = jsonMap.getString(key);
map.put(key, value);
}
return map;
}
For starters, The warning won't hurt you. It's just a heads up.
Also, make sure you have the syntax right. It should be:
#SuppressWarnings("unchecked") (you can place at the method level or right above the line you want to suppress.
Secondly, It's probably important to understand what the code is doing, because what you have works, but it reads a little oddly.
The warning basically says you have an iterator of an unknown type and you're assuming that it's an Iterator of Objects.
Then later in your code you cast all the keys to strings. You might as well just create an Iterator of Strings instead and skip the cast.
Iterator<String> keys = jsonMap.keys();
while (keys.hasNext()) {
String key = keys.next();
String value = jsonMap.getString(key);
map.put(key, value);
}
JSONObject#Keys() method returns Iterator of raw type and you're trying to assign it to reference of Iterator<Object>. Refer JSONObject.

Have complication with Map?

I have this question regarding generics.Can anybody explain me why the options 4 and 6[i know about this option]are correct?
Consider the following code:
import java.util.*;
public class TestClass
{
public static void main(String[] args)
{
// put declaration here
m.put("1", new ArrayList()); //1
m.put(1, new Object()); //2
m.put(1.0, "Hello"); //3
System.out.println(m);
}
}
How can 'm' be declared such that the above code will compile and run without errors?
Map m = new TreeMap();
Map<Object, Object> m = new TreeMap<Object, Object>();
Map<Object, ?> map = new LinkedHashMap<Object, Object>();
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>();will work
if lines //2 and //3 are commented out.
Map<Object, ? super ArrayList> m = new LinkedHashMap<Object, ArrayList>(); will work if lines //1 and //3 are commented out.
Map m = new HashMap();
For understanding this problem, look at the generic signature of the Map#put method you are using here. It states:
V put(K key, V value)
what means that you can put a key that is assignable to the generic type of the Map's key type K and a value that is assignable to the generic type of the Map's value type V. This must be true for all your key-value pairs you put into the map. From your code, you are putting the following keys into the map:
A String by the literal "1"
An Integer by the boxed int literal 1.
A Double by the boxed double literal 1.0.
The only common super type of these objects is the Object type which is required for K in order to allow all these objects to be used as a key.
For the values you have:
A ArrayList instance.
An Object instance
A String by the literal "Hello"
Again, the only common super type of these objects is the Object type which is required for V in order to allow all these objects to be used as a map value.
As a result, only Map instances with the generic signature Map<Object, Object> are permitted. What implementation of the Map interface you choose is up to you, as long as it is assignable to the variable type of the map, i.e. you can use a LinkedHashMap, a TreeMap or a HashMap since they only differ in the way they store their data. For the generic type of the variables, note that the use of wildcards ? or ? extends ... for your variable will result in you not being able to put values into the map anymore. The tutorial I linked explains why.
As for the types with a non-generic signature you are using, they behave similar to Maps with the generic signature <Object, Object>. Such raw types should however not longer be used after the introduction of Java 5.
With all this information, you can answer your (exam) question yourself.
Number 4 is correct for line 1, because "1" is String which has Object superclass and ? super ArrayList means that you can use ArrayList or any superclass of ArrayList.
Number 6 is correct because you are using untyped(raw) map, so it's similar to:
Map<Object, Object> m = new HashMap<Object, Object>();
To store such values you can use Map, but it's not a really good choice. You shouldn't use untyped collections at all. Think how you can change your design to not use such map.

Regarding querying Java Map with values as array of string.

I have map of string and array of strings as:
private static Map cacheTimeStamp = new HashMap<String, String[]>();
now how do i get value of this Map, i want to return array of String[] back to the calling function, tried using cacheTimeStamp.get("stringKey") but it returns object and i want to get array of strings out.
Define your map like this:
private static Map<String, String[]> cacheTimeStamp = new HashMap<String, String[]>();
Problem is that you are defining an open map without specifying map's key and value object types. Which is essentially a key of type java.lang.Object and a value of type java.lang.Object.
You didn't define the generics.
Map<String, String[]> cacheTimeStamp = new HashMap<String, String[]>();
It actually did return an array of Strings, but strictly you don't know when you don't include the generics in the definition. That's why the IDE tells you it will be an Object. Make sure you are beware of the fact that an array is an Object.
You actually get an String[].
I see two options:
Cast the return value to an String[]:
String[] myEntry = (String[]) cacheTimeStamp.get("stringKey");
(My preference) Add type arguments to the map:
private static Map cacheTimeStamp = new HashMap();
// ...
String[] myEntry = cacheTimeStamp.get("stringKey");

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;

Categories