0

I just realize that the compiler allows defining a map in multiple ways. Is there a difference between these declarations, and which is the "correct" way:

Map<String, String> data = new HashMap<String, String>();
Map<String, String> data = new HashMap<>();
Map<String, String> data = new HashMap();
yes4me
  • 159
  • 3
  • 15
  • Don't do number 3. It hides bugs like `Map data = new HashMap(mapOfIntegersUhOh);`. – user2357112 Dec 24 '16 at 00:56
  • They are all valid options but from different times of the JDK. The number 3 is from the days of JDK 4 and prior when there was no generics, the number 1 is from JDK 5 and possibly 6, when generics were overly verbose, the second one is that latest improvement of the compiler, the last stage of generics where it is less verbose. That's why all are valid. – Edwin Dalorzo Dec 24 '16 at 00:58
  • Maybe also relevant. http://stackoverflow.com/questions/16612157/why-does-new-hashmap-produce-an-error-in-jdk-1-6-but-not-1-7 – OneCricketeer Dec 24 '16 at 01:31

2 Answers2

1

The Third

Map<K, V> m = new HashMap();

This is the worst way to initialize m. HashMap here is a raw type, which means it ignores all generic data. It is cast to Map<K, V> safely, but the usage of the raw type causes a compiler warning anyway. This is for a good reason, as since generic data is being ignored, you can do this:

Map<String, String> m;
Map<Int, Int> m2 = new HashMap(m); // no error

Raw types exist for interoperability with old pre-Java 5 code, where generics didn't exist. All generic types are seen as their erasure. Therefore, the raw HashMap constructor no longer takes a Map<? extends K, ? extends V>, it takes a raw Map. The get method no longer takes a K to return a V, it takes an Object (erasure of K) and returns Object (erasure of V). This greatly impairs type safety, and the silent conversion of raw types to parameterized types does not help.

Never do it this way.

The First

Map<K, V> m = new HashMap<K, V>();

This is the pre-Java 7 way of initializing a generic type. Since the HashMap constructor is being passed type parameters, it is much safer than the third way. Generic information is now used to its full extend and everything is type safe. It is leaps and bounds better than the third, but you may as well do it the second way if you can.

The Second

Map<K, V> m = new HashMap<>();

This is the new and best way of initializing a generic type, introduced in Java 7. This is the same as the first way, except since it's a pain to write out the same type parameters on the left and right sides of a declaration, the type parameters may be omitted on the right. The <> is known as the diamond operator. Use this whenever you can.

TL;DR

The second way if you're on Java 7+, the first if you're not, and the third is too evil to even think about using.

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • I wish I could give credit also to the answer of thatguy. They are both great answers. I ended up picking this one because it is easier to read. – yes4me Dec 24 '16 at 02:22
  • @HTNW I believe the third option is only problematic if you don't use the no-arg constructor. In all other cases, the reference would ensure that the type safety applies. – Chetan Kinger Feb 13 '18 at 16:05
0
  1. Map<String, String> data = new HashMap<String, String>(); can be used since Java 5, where generics were added to the Java language, it is called a raw type.
  2. Map<String, String> data = new HashMap<>(); can be used since Java 7, where the diamond operator <> was introduced to make the declaration less verbose, as the type parameters for HashMap<> can be inferred from Map<String, String>. It is short for the first notation (sweet syntactic sugar).
  3. Map data = new HashMap(); was used until Java 5, because there were no generics, so you did not specify type parameters, keys and values were just Object.

To answer your question, always use option one or two, because you get stronger type safety. Option three is just an obsolete and dangerous way, which can lead to errors, as you could declare something like:

HashMap mixedMap = new HashMap();
mixedMap.put("bla", 20);
// Add further elements...

HashMap<String, String> map = new HashMap(mixedMap);

This works only, because of Java type erasure. At runtime, both mixedMap and map are seen as HashMap<Object, Object>, so casts are not safe anymore and the above construction is valid. However, if you use type parameters like in option one or two, you will get type safety warnings and errors thrown by the compiler.

So, why is the third option still possible, if it has that negative impacts? The answer is that the Java designers decided to maintain backwards compatibility, therefore they had to use type erasure, which unfortunately allows critical code concerning type safety.

thatguy
  • 21,059
  • 6
  • 30
  • 40