Why <String, String> entry allowed for HashMap<Integer, String>()? - java

I have HashMap which generic type <Integer, String> i.e. key should be an Integer and value should be String for this HashMap.
I wrote bellow code which put String and getting no compilation and runtime error. Why?
Map map = new HashMap<Integer, String>();
map.put("a", "one");
System.out.println(map);
OUTPUT:
{a=one}

I have HashMap which generic type <Integer, String> ...
No you do not!
Map map = new HashMap<Integer, String>();
Means you have just a Map (because of Map map =). If you want Map<Integer, String> you must use:
Map<Integer, String> map = new HashMap<Integer, String>();
or, in later versions of Java
Map<Integer, String> map = new HashMap<>();
Added
The reason for this is that the right-hand-side of the assignment is a separate process and is evaluated first. In your case it creates a HashMap<Integer, String>.
Next the assignment happens, the compiler checks that HashMap<Integer, String> can be cast to Map (which is equivalent to Map<Object,Object> BTW) and the assignment is performed. From then on all references to map treat it as type Map<Object,Object> and can therefore hold any type for key or value.

You are adding content to Map map declared without specifying any generics types.
If you declare the map this way the compilator doesn't know how to check the map content.
If you change your map declaration to
Map<Integer, String> map = new HashMap<>();
Then you will have a compilation error.

Map map = new HashMap<Integer, String>();
Here your definition is type specified, however declaration is not. So, you are able to add any type to map.
The proper way for generic map declaration is
Map<Integer, String> map = new HashMap<Integer, String>();
or in new versions of Java, you can skip type in defintion.
Map<Integer, String> map = new HashMap<>();

Defining generics on the right side is more or less obsolete (grey font).
Following code wouldn't compile:
Map<Integer, String> map = new HashMap<>();
map.put("a", "one");
System.out.println(map);
with this explanation:
Wrong 1st argument type. Found: 'java.lang.String', required: 'java.lang.Integer'

Related

Generics Handling LinkedHashMap<String, LinkedHashMap>

I have never been that good in Generics but I used SnakeYaml.
Is there a way to let me fix this code
public class MyService{
private static Map<String, LinkedHashMap> myYamlMap;
public static void filter(Map<String, String>){
//myYaml map reads the YAML File using SnakeYaml
//Snake Yaml returns data in this format <String,LinkedHashMap>
Yaml yaml = new Yaml();
Object object = yaml.load(reader);
Map<String, LinkedHashMap> myYamlMap = (Map<String, LinkedHashMap>)object;
LinkedHashMap<String, LinkedHashMap> mainMap = (LinkedHashMap<String, LinkedHashMap>)myYamlMap.get("sample");
}
}
and get away with this compile time warnings?
Multiple markers at this line
- Line breakpoint:MyService [line: 69] - filter(Map<String, String>)
- Type safety: Unchecked cast from LinkedHashMap to LinkedHashMap<String,LinkedHashMap>
- LinkedHashMap is a raw type. References to generic type LinkedHashMap<K,V> should be
parameterized
- LinkedHashMap is a raw type. References to generic type LinkedHashMap<K,V> should be
parameterized
Snakeyaml..uses LinkedHashMap in its construct and I wanted to get away with the casting.
Given your code:
private static Map<String, LinkedHashMap> myYamlMap;
LinkedHashMap<String, LinkedHashMap> mainMap = (LinkedHashMap<String, LinkedHashMap>)myYamlMap.get("sample");
This doesn't make sense.
private static Map<String, LinkedHashMap> myYamlMap;
This should probably be
private static Map<String, Map<Key, Value>;
myYamlMap = new LinkedHashMap<String, Map<Key, Value>>;
myYamlMap.put("key1", new LinkedHashMap<Key,Value>();
for some Key and Value types, which aren't specified in your code...
OR something more complex -- see below
LinkedHashMap<String, LinkedHashMap> mainMap = (LinkedHashMap<String, LinkedHashMap>)myYamlMap.get("sample");
Your use of get here seems to imply that myYamlMap should be
private static Map<String, Map<String, Map<Key, Value>> myYamlMap;
myYamlMap = new LinkedHashMap<String, Map<String, Map<Key,Value>>>;
Map<Key,Value> temp = new LinkedHashMap<Key,Value>();
temp.put(k1, value1);
myYamlMap.put("sample", temp);
since you seem to be expecting get() to return a Map<String,Map<Key,Value>> from within the outer collection.
NOW you can do
Map<String, Map<Key,Value>> mainMap = myYamlMap.get("sample");
The reason for using the Map interface is that nowhere in your code do you use methods specific to LinkedHashMap so declarations should all be using just Map<...> except when instantiating the maps.

Display first records in a sorted Map

I'm trying to display first 40 records out of 17K I have stored in a map. I have the following code
import java.util.*;
Map<String, Integer> doubleCount= new HashMap<String,Integer>();
....
Map<String,Integer> newDouble40 = doubleCount.headMap(40);
Java is giving me the following error:
" cannot find symbol - method subMap...
so I tried:
Map<String,Integer> newDouble40 = doubleCount.subMap("",(Integer)40);
and the exact error was:
cannot find symbol - method subMap(java.lang.String,java.lang.int)
http://docs.oracle.com/javase/7/docs/api/java/util/SortedMap.html
how do I sort?
subMap() and headMap() are two methods in SortedMap those are not available in Map
You can try following way
Map<String, Integer> doubleCount= new HashMap<String,Integer>();
SortedMap<String, Integer> newMap= new TreeMap<>(doubleCount);
Map<String,Integer> newDouble40 = newMap.subMap("0","40");
In your case Keys are String so you need to have String values in subMap("0","40"). 0 is the starting key and "40" is the ending key. Your newDouble40 has element which has a key in between 0 and 40.
Here you can use headMap() as newMap.headMap("40"). now you will get elements which has a key less than 40.
Eg:
Map<String, Integer> doubleCount= new HashMap<>();
doubleCount.put("c",1);
doubleCount.put("d",2);
doubleCount.put("a",1);
doubleCount.put("b",4);
SortedMap<String, Integer> newMap= new TreeMap<>(doubleCount);//sorted now
Map<String,Integer> map1 = newMap.subMap("a", "c");
Map<String,Integer> map2 = newMap.headMap("c");
System.out.println(map1);
System.out.println(map2);
Out put:
{a=1, b=4}
{a=1, b=4}
Main issue here is that you're trying to use methods of a subinterface (java.util.SortedMap), the interface Map doesn't expose either headMap(...) or subMap(...) methods.
A correct code that will compile would be:
SortedMap<String, Integer> doubleCount = new TreeMap<String, Integer>();
Map<String, Integer> newDoubleCount = doubleCount.headMap("40");
One thing you should consider is what is that the methods of the SortedMap returns a portion of the map based on the key argument, compared with the keys in the map, unless you know what is the value of the key of the 40th element, you can't use these methods.

Can I use type of a variable to declare another variable in Java?

Can I do something like this in Java?
HashMap<String, Child> childMap=new HashMap<String, Child>();
HashMap<String, childMap.typeName> parentMap=new HashMap<String, childMap.typeName>();
//instead of
HashMap<String, HashMap<String, Child>> parentMap=new HashMap<String, HashMap<String, Child>>();
or something like this
HashMap<String, HashMap<String, Child>> parent1=new HashMap<String, HashMap<String, Child>>();
parent1.typeName parent2=new parent1.typeName;
Because some time, if the map level is too deep or too complex, it is very hard to write and read.
Abbreviations are possible by defining a subclass:
class Str2Child extends HashMap<String, Child>>{}
class Str2Map extends HashMap<String,Str2Child>{}
Str2Map parent1 = new Str2Map();
No but you could shorten it if you're using Java 7 or higher. The compiler can infer the type parameters from the left side of the assignment and you can skip them altogether while creating the object HashMap<String, HashMap<String, Child>> parentMap = new HashMap<>();
In older versions of Java, you could resord to Guava's Maps class and its newHashMap method. HashMap<String, HashMap<String, Child>> parentMap = Maps.newHashMap();
Another thing you could possibly do is create a type that implements a certain specification of the generic HashMap.
public class HashMapStringChild extends HashMap<String, Child> {
}
and then use it as a type parameter
HashMap<String, HashMapStringChild> parent2 = new HashMap<>();
but personally, I find this a bit of a stretch. I certainly wouldn't overuse it and I'd be careful extending the collection classes.
Addendum
You should also note that you're effectively binding your API to a specific implementation of the Map interface (HashMap), or even worse, in case of introducing the new class (HashMapStringChild), to a specific, non-standard implementation.
What if at some point, you decide to keep your Child objects sorted at all times? You could do this by switching to a TreeMap but that would mean a big deal of refactoring.
You would be better off basing your API on a more general interface. This way you could switch from
Map<String, Map<String, Child>> map = new HashMap<String, HashMap<String, Child>>();
to
Map<String, Map<String, Child>> map = new HashMap<String, TreeMap<String, Child>>();
or
Map<String, Map<String, Child>> map = new TreeMap<String, TreeMap<String, Child>>();
or any other implementation without a hassle.
If you really want to make the map of String to Child a specific type, you could introduce an interface
public interface MapStringToChild extends Map<String, Child> {
}
Then you could keep your reference types general and use HashMap<String, Child>, TreeMap<String, Child>, HashMapStringChild or literally any other implementation mapping a String to a Child interchangeably, while keeping the code short.

Initializing a Map with List inside

I need to use a Map with a List inside :
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I am getting compiler error on this line in eclipse.
The only working thing seem to be changing the inside List in the Map to ArrayList
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I had to change the signature of many interfaces' methods, but I still don't get it; why isn't the first definition work?
Isn't it the same, should not
Map<String, List<String>> keyToGroup
&
Map<String, ArrayList<String>>
be the same?
No, they're not. Consider this:
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
keyToGroup.put("foo", new LinkedList<String>());
The second line is fine, because a LinkedList<String> is a List<String> - but it's not logically fine in terms of adding it to a HashMap<String, ArrayList<String>>, because a LinkedList<String> is not an ArrayList<String>.
To make it clearer:
Map<String, ArrayList<String>> map1 = new HashMap<String, ArrayList<String>>();
Map<String, List<String>> map2 = map1; // This is invalid
map2.put("foo", new LinkedList<String>());
ArrayList<String> oops = map1.get("foo"); // Because this would be broken
This isn't just the case with collections as the type argument. It's even simpler to see with normal inheritance:
List<Banana> bunchOfBananas = new ArrayList<Banana>();
List<Fruit> fruitBowl = bunchOfBananas; // Invalid!
fruitBowl.add(new Apple());
Banana banana = bunchOfBananas.get(0);
Even though every banana is a fruit, so a "collection of bananas" is a "collection of fruit* in the sense of fetching them, not every fruit is a banana.
You can use wildcard parameterized types to help in some cases, but it depends on exactly what you're trying to achieve.
Ask yourself a question if you need particular list implementation in your Map or any List?
In case of particular implementation you can use your last example:
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
In case of any list just use:
Map<String, List<String>> keyToGroup = new HashMap<String, List<String>>();
keyToGroup.put("arraylist", new ArrayList<String());
keyToGroup.put("linkedlist", new LinkedList<String());
BTW the second option usually is better from design point of view so if you don't know exactly for now - try using second option first.
No they are not. Generics are not covariant in Java.
If they are covariant you can logically put any type of List instead of ArrayList which defeats the purpose of having generics.
Consider reading Effective Java (2nd Edition) Chapter 5: Generics which has very good explanation of Generics.
Another good read is http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

How to remove elements of one map from another map?

HashMap<String, String> foo = new HashMap<String, String>();
HashMap<String, String> baar = new HashMap<String, String>();
How to remove items found in baar from foo?
You can try:
foo.keySet().removeAll(baar.keySet())
Changes to a Map's keySet() are reflected in the map itself.
If you want to remove exact mappings (not just based on keys), you can use the same approach with the entrySet() instead:
foo.entrySet().removeAll(baar.entrySet());

Categories