What is the difference between
Map map = new HashMap<Long, String>();
and
Map map = new HashMap<>();
Which is better for use and why?
The specification HashMap<Long, String>() is simply for the parameter types. But both the use cases you have shown are bad (I would just call it wrong) because the map object has no parameter types. What you should be doing is
Map<Long, String> map = new HashMap<Long, String>();
which is identical to
Map<Long, String> map = new HashMap<>();
The only difference between the two lines is whether you want to rely on automatic type inference of the JDK.
Addendum:
As Boris confirmed, in pre-JDK7, the automatic type inference was not possible, and the second way of initializing the map would not have compiled.
If you are using an IDE (really hope you are), please pay attention to warning about raw use of parametrized types.
Really it should be Map<Long, String> map = new HashMap<>(); The 2 fields in the <> represent the key and value type of the map. So in this example it's a map of Strings which a referenced by Long values. Including them in the declaration forces the Map to ensure that the map can only have specific Key/Value types. It used to be that the types had to be included in the <> of the assignment but it hasn't been needed since Java 7.
Related
I already know how to initialize Java HashMap by using one of the following 2 ways
// way 1: apply generic type saftey
HashMap<String, Integer> hashMap1 = new HashMap<String, Integer>();
// way 2: general without apply generic type saftey
HashMap<String, Integer> hashMap2 = new HashMap();
My problem
What is the best practice
According to Eclipse Marker
Type safety: The expression of type HashMap needs unchecked conversion
to conform to HashMap
So Its recommend to use
new HashMap<String, Integer>();
But according to Sonar Linter
Replace the type specification in this constructor call with the
diamond operator ("<>").
So Its recommend to use
new HashMap();
Which one is the best? Why?
Use the Java 7 diamond operator:
HashMap<String, Integer> hashMap2 = new HashMap<>();
Diamond <> allows the compiler to infer types implicitly
See: Type Inference for Generic Instance Creation
Considering this piece of code, in which I have two maps, a Map<String, Integer> and a Map<String, String>. I assign the second one to an Object and cast this object to a Map so that I can putAll this map to the first one.
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
Map<String, String> otherMap = new HashMap<>();
otherMap.put("two", "two");
Object obj = otherMap;
map.putAll((Map<String,Integer>)obj);
Assert.assertFalse(map.get("two") instanceof Integer);
Assert.assertEquals("{one=1, two=two}", map.toString());
The first assert ensures that the second element of my Map is not a Integer, so how come the putAll did not fail ?
The second assert is jus here to show that there is no apparent problem to this map.
How can I make sure that the putAll method will fail, when the map is first assigned to an Object ?
Thanks
Generics are a compile-time feature, and are not enforced at runtime. Your code would compile with unchecked warnings telling you exactly this: that your code might behave unexpectedly.
To massively oversimplify, all Maps are treated as a Map<Object, Object> at runtime. (Not really, but sort of.)
You could, if you really wanted, use Collections.checkedMap to wrap the map and enforce the type-safety at runtime. There's an associated performance cost though.
I'm trying Kotlin and I've encountered a small problem that I can't resolve.
When I have the following construction I can put elements into the map:
val map = HashMap<String, String>()
map["asd"] = "s"
map.put("34", "354")
However when I create a map with the Map interface I can only read them, what I'm doing wrong ?
val map: Map<String, String> = HashMap<String, String>();
map.put("24", "34") //error
map["23"] = "23" //error
Or maybe I'm confusing something about interfaces in Kotlin ?
In the first example map gets the type of HashMap,
in the second example you cast it to the Interface Map.
Map is a readonly map, there is no put/set, see here
In order to be able to edit the map, you should use MutableMap
When working with kotlin collections, one important consideration is that, kotlin categorizes its collections as mutable and immutable. this is in contrast to java, where no such categorization exists.
In kotlin for most collections you have a base interface which only supports read-only methods. In your case Map<K,V is an example of that, from the docs
Methods in this interface support only read-only access to the map;
read-write access is supported through the MutableMap interface.
this is the reason for error when you try to modify the map after val map: Map<String, String> = HashMap<String, String>();, even though the actual object is of type HashMap<String,String>, but the map reference is of type Map<String,String>, which will only provide read only operation.
Now if you use a class which implements MutableMap<K,V> then you can put values in map as well. this is the case with val map = HashMap<String, String>(), since here type of map is HashMap<K,V>, which extends MutableMap<K,V> and hence is mutable.
I use this common initialization format when I anticipate changing the implementation of the List interface at a later time:
List<Foo> foos = new ArrayList<Foos>();
In an effort to gain the same utility for the values within a Map, I attempted the following but my compiler whines about List<> and ArrayList<> being incompatible types.
Map<String, List<Foo>> fooMap = new HashMap<String, ArrayList<Foo>>;
I've been unable to find an explanation for why I cannot initialize the map in this manner and I'd like to understand the reasoning.
And, sure, this works...
Map<String, List<Foo>> foosMap = new HashMap<String, List<Foo>>;
// ... populate map
ArrayList<Foo> foosAryLst = (ArrayList)foosMap.get("key1");
... but I'm a curious castaphobe. I'd rather fix compile-time errors than runtime errors, things like this aggravate my OCD and the smell of casting conjures an odor similar to the urinal trough after free deep-fried asparagus night at the stadium.
My questions come down to:
Why can I not code my map values to an interface.
Is there a workaround that doesn't require casting?
Any input will be appreciated, thanks!
Sure, there's a workaround that doesn't require casting: don't cast; write
List<Foo> foosLst = foosMap.get("key1");
...and code to the interface with the List as well as the Map.
The root issue, though, is that a Map<String, ArrayList<Foo>> isn't substitutable wherever you'd use Map<String, List<Foo>>. In particular,
Map<String, List<Foo>> map = new HashMap<>();
map.put("foo", new LinkedList<Foo>());
works, but not if map is a Map<String, ArrayList<Foo>>. So one isn't a drop-in substitute for the other.
The declaration that you proposed
Map<String, List<Foos>> fooMap = new HashMap<String, ArrayList<Foos>>();
simply does not make sense: The variable fooMap has the type Map<String, List<Foos>>. This means:
every value that you obtain from this map is a List<Foos>
you may put every value into this list that is (of a subtype of) List<Foos>
If you wanted a map that has ArrayLists as its values, then you would declare it as
Map<String, ArrayList<Foos>> fooMap = new HashMap<String, ArrayList<Foos>>();
If you don't care about the list type, then you can say
Map<String, List<Foos>> fooMap = new HashMap<String, List<Foos>>();
But there's no sensible meaning of mixing the two. Even if you could write what you proposed, then you could still not obtain an ArrayList from this map, because this is simply not the type that fooMap was declared with.
In most cases,
Map<String, List<Foos>> fooMap = new HashMap<String, List<Foos>>();
should be appropriate. Depending on the use case, one could possibly go further by saying
Map<String, List<? extends Foos>> fooMap = new HashMap<String, List<? extends Foos>>();
This way, you can also put lists into the map that contain sublcasses of Foos, like
List<SpecialFoos> specialFoos = ...
fooMap.put("special", specialFoos);
But of course, it's up to you to decide whether this is necessary or not.
The core of the problem is that the compiler cannot keep track of what fooMap may have been assigned to at any particular point in the execution of your code, so there is no way for the compiler to know that
fooMap.put("abc", new ArrayList<Foo>())
should be legal, but that
fooMap.put("abc", new LinkedList<Foo>())
should not be.
All that the compiler knows about the typing of fooMap is its declared type Map<String, List<Foo>>. So, it enforces that whatever object to which you assign fooMap must be able to support all of the operations which a generic Map<String, List<Foo>> is capable of executing. The second line of code above is clearly legal for a Map<String, List<Foo>>, but not legal for a Map<String, ArrayList<Foo>>, so the compiler forbids you from assigning fooMap to a Map<String, ArrayList<Foo>>.
I see many examples about multimap but did not understand why Google Gauva is different?
Multimap<Integer, Set<String>> option4 = HashMultimap.create(); // Gauva
Map<Integer, Set<String>> opt = new HashMap<Integer, Set<String>>(); //Core Java
Is both above are behave same for holding data or different?
A MultiMap<A, B> associates a key of type A with a value of type Collection<B> (hence the name MultiMap)
A Map<A, B> associates a key of type A with a value of type B.
So, a MultiMap<Integer, Set<String>> can be viewed as a Map<Integer, Collection<Set<String>>. This should be obvious by reading the api documentation.
The difference is that with the second, Core Java implementation, you need to check whether the Set is there before you insert. Guava's Multimap takes care of that for you.
With Core Java:
Set<String> innerSet = opt.get(key);
if (innerSet == null) {
innerSet = new HashSet<String>();
opt.put(key, innerSet);
}
innerSet.add(value);
With Guava:
opt.put(key, value);
Guava takes care of initialising an otherwise absent Set to store the values, takes care of any threading issues (eg stops two threads from creating a new Set for the same key in parallel) and also provides a few useful methods that you'd otherwise need to implement by hand, such as getting all the values across all the Sets.
You misunderstood something. These are not even roughly equivalent:
Multimap<Integer, Set<String>> option4 = HashMultimap.create(); // Guava
Map<Integer, Set<String>> opt = new HashMap<Integer, Set<String>>(); //Core Java
In your example, opt4 would map a single Integer to a Collection of Sets of Strings.
That's exactly the point of using a Multimap, you don't have to explicitly deal with the second dimension. So in fact, the correct (equivalent) declaration would be:
SetMultimap<Integer, String> multimap = HashMultimap.create(); // Guava
and you can get a map view like this:
Map<Integer, Set<String>> mapView = multimap.asMap();
Nope, MultiMap means that there would be a collection of objects attached to each key.
Documentation: Multimap_Is_Not_A_Map
First of all com.google.common.collect.Multimap is not java.util.Map, it is in a separate hierarchy.
Secondly, you can all the operations with Map<Integer, Set<String>> that Multimap interface requires but you will have to implement them yourself while HashMultimap offers ready implementation.