Is this possible in Java: Map<SomeObject, Map<SomeOtherObject>>? - java

Is this possible in Java: Map<SomeObject, Map<SomeOtherObject>>? I'm trying Map<Integer, Map<String>> am getting an
"Incorrect number of arguments for
type Map; it cannot be
parameterized with arguments "
error.

Every Map needs to be parametrized on two types; your second (nested) Map has only one.

A Map maps keys to values, so Map<String> is incorrect. So you'd need something like Map<String, Object>.

You need a second argument on your second Map<>. Perhaps you mean Map<Integer, Map<String, String>>?

No, not really like that. You need to give a type for both Key and Value for the second "inner" Map, this is ok:
Map<SomeObject, Map<SomeOtherObject, Object>>
Just like with the outer Map, where the Key is SomeObject, and Value is the inner Map. So, if you add a value specification for the inner Map is, that would be ok.

Related

Using interfaces instead of concrete data structures in Java Collection API

I am currently reading "Core Java for the Impatient" by Horstmann (I recommend it, love the concise style) and I have a trouble understanding one of the exercises pertaining to the collection API. The exercise is as follows:
I encouraged you to use interfaces instead of concrete data structures, for example, a
Map instead of a TreeMap. Unfortunately, that advice goes only so far. Why can’t
you use a Map<String, Set<Integer>> to represent a table of contents?
(Hint: How would you initialize it?) What type can you use instead?
The following code compiles and works without a problem though, even though the interfaces were used for variable declarations. What am I missing?
Map<String, Set<Integer>> toc = new HashMap<>();
toc.put("element1", IntStream.of(1, 2, 3).boxed().collect(Collectors.toSet()));
toc.put("element2", IntStream.of (3, 4, 7).boxed().collect(Collectors.toSet()));
toc.forEach( (k, v) -> {
System.out.print(k + " ");
v.forEach(val -> System.out.print(val + " "));
System.out.println();
} );
}
An interface like Map is a supertype for all interfaces that inherit it and all classes that implement it. So TreeMap inherits from Map, and because you can always assign to a variable any reference that is of a subtype, it's perfectly acceptable to assign a TreeMap reference to a Map variable. This is called a widening reference conversion
https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.5
"Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time."
So, yes, you certainly can use a Map<String, Set<Integer>> to represent something in your domain model, but you can't instantiate an interface directly; you must instantiate a concrete type (class) that implements it. This is exactly what you did when you declared
Map<String, Set<Integer>> toc = new HashMap<>();
As an extension of this principle, you could just as easily write
AbstractMap<String, Set<Integer>> toc = new HashMap<>();
since AbstractMap is also a supertype of HashMap.
In general you want to declare the widest type for the variable that can hold the largest possible set of subtype references that work in your logic. If you need a sorted map, then 'Map' is too wide; it doesn't enforce sortedness. You'd have to declare the variable as TreeMap , or better, SortedMap for that.
Usually the interface is the widest applicable type, but you have to think about it in case it's not.
EDIT: Mentioned SortedMap in light of comment.
I have got in touch with book's author and he agreed that question was unclear and it was to lead the reader to use wildcard types. The exercise in question was changed to:
Suppose you have a method parameter of type Map<String, Set<Integer>>, and someone calls your method with a HashMap<String, HashSet<Integer>>. What happens? What parameter type can you use instead?
The answer is that in this case one should use the wildcard type: Map<String, ? extends Set<Integer>>.

Are there performance benefits to creating a Hashmap with defined types vs. Objects?

When should I do one, and when should I do the other, especially in cases where both suffice? For example, consider if I need a Hashmap of type <String, String>. Is there any reason to do a hashmap of type <Object, Object>?
Are there performance benefits/penalties for either, or is it an issue of clarity?
I suppose you are using HashMap like this:
Map map = new HashMap();
map.put("aKey","value");
String v =(String)map.get("aKey");
You can use it like this:
Map<String,String> map= new HashMap<String,String>();
map.put("aKey","value");
String v = map.get("aKey");
Because it is used as a polymorphic reference. You want String I may want my own custom Class. So make it generic they have used Objects. However you can use generics to avoid cast.
That depends on how you initialize it. According to the JavaDocs, the HashMap can take the data types of what it is storing, so if you do this:
Map hashMap = new HashMap();
map.put("hello", "abc");
You would need to cast to get back your keys and data. However, if you do something like so:
Map<String, String> hashMap = new HashMap<>();
map.put("hello", "abc");
You no longer need to cast the objects that you have. You can take a look here for more information on Generics.
//If you dont make generic then you have to cast the object
HashMap myMap = new HashMap();
// If you make it generic then you dont have to cas the object
HashMap<String, String> myMap2 = new HashMap<String, String>();
An additional clarification is that .... objects are never stored inside a HashMap. Instead the reference/identity of the object is kept inside.
On retrieval, actual reference is picked from the location (given by HashMap) and provided to caller.
Purpose of Java generics is to apply compile time checks only; it has noting to do with run-time.
What if reference points to an Integer and type-cast is expecting a String?
Generics simplify the programming and helps in avoiding Class Cast errors at run-time.

Java HashMap<String, String> has LinkedHashMap in value

So I have a strange issue with a HashMap and not sure how to access the value. I have a HashMap returned to me from an API that is suppose to be HashMap<String, String>. One of the values is actually a LinkedHashMap. I've tried casting it, but since it is a HashMap that is <String, String> it's giving me an error that it's not possible. Is there anyway to get a LinkedHashMap value out of a HashMap that is <String, String>?
I have tried this with no luck: ((HashMap)userInfo.get("key")).get("key");
Could not complete request <java.lang.ClassCastException:
java.util.LinkedHashMap cannot be cast to java.lang.String>java.lang.ClassCastException:
java.util.LinkedHashMap cannot be cast to java.lang.String
This is really ugly looking, but I was actually able to get it out of the HashMap with this:
(HashMap) ((HashMap)((HashMap)userInfo).get("picture")).get("data");
Thanks to Jeroen for sending me down the right path.
Brackets placement.
Try
(((HashMap)userInfo).get("key")).get("key");
You have to cast before you use .get() (assuming your attempt is actually valid and it is just a matter of brackets).
Convert typed map first to untyped map and then check type of each value. Map interface is implemented by HashMap and LinkedHashMap classes so you'll most likely want to use it instead of more specific types.
HashMap<String, String> typedMap = ...
Map untypedMap = (Map) typedMap;
Object mapValue = untypedMap.get("key");
if(mapValue instanceof Map) {
// handle as Map
}
if(mapValue instanceof String) {
// handle as String
}
If it is a LinkedHashMap and you cast it to a HashMap you will have probelms.
You could instead treat it as a Map:
Map m = userInfo.get("key");

how to declare variable like this-- ArrayList LinkedhashMap

how to declare variable like this-- ArrayList LinkedhashMap.
Map<String, ArrayList<LinkedHashMap<KeyType, ValueType>> maps;
KeyType and ValueType are placeholders, I don't know the real types. And the real declaration should use interfaces. But that's the closest to answer your question.
(The better declaration:
Map<String, List<Map<KeyType, ValueType>> maps;
We map lists of maps to string values. That's the explanation for this datastructure
)
Well if you are using Java 1.5 or greater , you can make use of Generics
//This approach is type safe.
List<LinkedHashMap<KeyType,ValueType>> myListOfMaps = ArrayList<LinkedHashMap<KeyType, ValueType>>();
for Java less that 1.5 use normal Arraylist.
List myListOfMaps = new ArrayList (); //But with this approach its not type safe ,
//because any object type can be inserted
Now to create a Map of ListsofMaps
Map<String, List<Map<KeyType, ValueType>> maps = new HashMap <String, List<Map<KeyType, ValueType>>();
maps.put("rows",myListOfMaps );
This link will give you Why Use Generics ?
Check this Generic tutorial for more info

What is the difference between instantiating hashmap without angle brackets and with?

What is the difference between the following?
new HashMap(); vs new HashMap<Integer, String>();?
I've never used the former or seen it but is there any differences I should know?
First one doesnt know what type of data hashmap has. You can have any type of object in this map. Second one specifies what is key and value types for this map. You can only insert those types into map. This approach is called generics.
Following statement will creates a map whose key-value types are raw. (java.lang.Object).
HashMap map=new HashMap();
In second statement, you are specifying type of Key and Value (Read Java Generics) .
HashMap<Integer,String> map=new HashMap<Integer,String>();
It should be new Hashmap<Integer, String>().
The first case will use Object type for keys as well as values and will return Object instances when you retrieve them. In the second case, you are specifying that the keys should be of Integer type and the values are of String type. So, you should add accordingly and when you retrieve, you'll get String object for values and Integer object for keys and you will not need a cast as in the previous case.
P.S. I think the reason you should use Integer and not int is that the type used should be "nullable" if some method needs to return null. int is not nullable.

Categories