There is no difference between the objects; you have a HashMap<String, Object> in both cases. There is a difference in the interface you have to the object. In the first case, the interface is HashMap<String, Object>, whereas in the second it's Map<String, Object>. But the underlying object is the same.
The advantage to using Map<String, Object> is that you can change the underlying object to be a different kind of map without breaking your contract with any code that's using it. If you declare it as HashMap<String, Object>, you have to change your contract if you want to change the underlying implementation.
Example: Let's say I write this class:
class Foo {
private HashMap<String, Object> things;
private HashMap<String, Object> moreThings;
protected HashMap<String, Object> getThings() {
return this.things;
}
protected HashMap<String, Object> getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
The class has a couple of internal maps of string->object which it shares (via accessor methods) with subclasses. Let's say I write it with HashMaps to start with because I think that's the appropriate structure to use when writing the class.
Later, Mary writes code subclassing it. She has something she needs to do with both things and moreThings, so naturally she puts that in a common method, and she uses the same type I used on getThings/getMoreThings when defining her method:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Later, I decide that actually, it's better if I use TreeMap instead of HashMap in Foo. I update Foo, changing HashMap to TreeMap. Now, SpecialFoo doesn't compile anymore, because I've broken the contract: Foo used to say it provided HashMaps, but now it's providing TreeMaps instead. So we have to fix SpecialFoo now (and this kind of thing can ripple through a codebase).
Unless I had a really good reason for sharing that my implementation was using a HashMap (and that does happen), what I should have done was declare getThings and getMoreThings as just returning Map<String, Object> without being any more specific than that. In fact, barring a good reason to do something else, even within Foo I should probably declare things and moreThings as Map, not HashMap/TreeMap:
class Foo {
private Map<String, Object> things; // <== Changed
private Map<String, Object> moreThings; // <== Changed
protected Map<String, Object> getThings() { // <== Changed
return this.things;
}
protected Map<String, Object> getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Note how I'm now using Map<String, Object> everywhere I can, only being specific when I create the actual objects.
If I had done that, then Mary would have done this:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...and changing Foo wouldn't have made SpecialFoo stop compiling.
Interfaces (and base classes) let us reveal only as much as is necessary, keeping our flexibility under the covers to make changes as appropriate. In general, we want to have our references be as basic as possible. If we don't need to know it's a HashMap, just call it a Map.
This isn't a blind rule, but in general, coding to the most general interface is going to be less brittle than coding to something more specific. If I'd remembered that, I wouldn't have created a Foo that set Mary up for failure with SpecialFoo. If Mary had remembered that, then even though I messed up Foo, she would have declared her private method with Map instead of HashMap and my changing Foo's contract wouldn't have impacted her code.
Sometimes you can't do that, sometimes you have to be specific. But unless you have a reason to be, err toward the least-specific interface.
Answer from T.J. Crowder on Stack OverflowThere is no difference between the objects; you have a HashMap<String, Object> in both cases. There is a difference in the interface you have to the object. In the first case, the interface is HashMap<String, Object>, whereas in the second it's Map<String, Object>. But the underlying object is the same.
The advantage to using Map<String, Object> is that you can change the underlying object to be a different kind of map without breaking your contract with any code that's using it. If you declare it as HashMap<String, Object>, you have to change your contract if you want to change the underlying implementation.
Example: Let's say I write this class:
class Foo {
private HashMap<String, Object> things;
private HashMap<String, Object> moreThings;
protected HashMap<String, Object> getThings() {
return this.things;
}
protected HashMap<String, Object> getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
The class has a couple of internal maps of string->object which it shares (via accessor methods) with subclasses. Let's say I write it with HashMaps to start with because I think that's the appropriate structure to use when writing the class.
Later, Mary writes code subclassing it. She has something she needs to do with both things and moreThings, so naturally she puts that in a common method, and she uses the same type I used on getThings/getMoreThings when defining her method:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Later, I decide that actually, it's better if I use TreeMap instead of HashMap in Foo. I update Foo, changing HashMap to TreeMap. Now, SpecialFoo doesn't compile anymore, because I've broken the contract: Foo used to say it provided HashMaps, but now it's providing TreeMaps instead. So we have to fix SpecialFoo now (and this kind of thing can ripple through a codebase).
Unless I had a really good reason for sharing that my implementation was using a HashMap (and that does happen), what I should have done was declare getThings and getMoreThings as just returning Map<String, Object> without being any more specific than that. In fact, barring a good reason to do something else, even within Foo I should probably declare things and moreThings as Map, not HashMap/TreeMap:
class Foo {
private Map<String, Object> things; // <== Changed
private Map<String, Object> moreThings; // <== Changed
protected Map<String, Object> getThings() { // <== Changed
return this.things;
}
protected Map<String, Object> getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Note how I'm now using Map<String, Object> everywhere I can, only being specific when I create the actual objects.
If I had done that, then Mary would have done this:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...and changing Foo wouldn't have made SpecialFoo stop compiling.
Interfaces (and base classes) let us reveal only as much as is necessary, keeping our flexibility under the covers to make changes as appropriate. In general, we want to have our references be as basic as possible. If we don't need to know it's a HashMap, just call it a Map.
This isn't a blind rule, but in general, coding to the most general interface is going to be less brittle than coding to something more specific. If I'd remembered that, I wouldn't have created a Foo that set Mary up for failure with SpecialFoo. If Mary had remembered that, then even though I messed up Foo, she would have declared her private method with Map instead of HashMap and my changing Foo's contract wouldn't have impacted her code.
Sometimes you can't do that, sometimes you have to be specific. But unless you have a reason to be, err toward the least-specific interface.
Map is an interface that HashMap implements. The difference is that in the second implementation your reference to the HashMap will only allow the use of functions defined in the Map interface, while the first will allow the use of any public functions in HashMap (which includes the Map interface).
It will probably make more sense if you read Sun's interface tutorial
For Java 9 and beyond, you can make use of Map#of, as well as Map#ofEntries with Map#entry:
Map<String, String> example = Map.ofEntries(
Map.entry("1", "one"),
Map.entry("2", "two"),
Map.entry("3", "three"),
//...
);
For shorter literals (10 or less entries), you can use Map#of directly (albeit with a slightly confusing syntax of Key1, Value1, Key2, Value2, ...):
Map<String, String> example = Map.of("1", "one", "2", "two");
Prior to Java 9, this would typically only be needed for initializing constant Map objects, for which a static initializer block is also useful:
private static final Map<String, String> MY_CONSTANT;
static {
Map<String, String> setup = new HashMap<>();
setup.put("1", "one");
setup.put("2", "two");
setup.put("3", "three");
MY_CONSTANT = Collections.unmodifiableMap(setup);
}
This will allow you to create a constant Map while allowing you to modify the map before it is set.
As pointed out by @Pshemo, there are numerous reasons to avoid double-brace initialization, however the notable exception to the "rule" is typically in constants much like the above example (as numerous anonymous classes would not be made). However, it is still possible to work around the need for double-brace initialization even in this case, so it is overall best to avoid if possible. The only case I can think of this not being possible is within the context of a constant within an enum class, where the double-brace init may truly be the only true way to make this work.
Firstly, you shouldn't use Hashtable class - it's legacy. If you need a general purpose implementation of the Map interface, use HashMap instead.
In case when you need to initialize a general purpose Map with a few entries, you can use one of the overloaded versions of Map.of() method which is available starting with Java 9. It allows providing up to 10 key-value pairs, if you need more you can use Map.ofEntries(), which takes a varargs of map entries. You can create a map entry using another Java 9 method Map.entry().
Example:
public static final Map<String, Integer> numbers =
Map.of("one", 1, "two", 2, "three", 3);
Note these maps are
1. Immutable, therefore it would be the right choice only if the data never change (or might change rarely, i.e. in case of very frequent reads and infrequent writes, and every write would require re-initializing the map).
2 So-called unmodifiable maps like MapN (which is an internal implementation used by Map.ofEntries() intended to store an arbitrary number of key-value pairs) are optimized in terms of Memory consumption, their underlying array occupies exactly as match space as match there are key-value pairs and no more, and retrieving a value from an unmodifiable map using get() might even perform slightly better (see the quote below) in comparison to a HashMap.
For more information you can refer to official documentation - Creating Immutable Lists, Sets, and Maps:
Use Cases
The common use case for the immutable methods is a collection that is initialized from known values, and that never changes. Also consider using these methods if your data changes infrequently.
For optimal performance, the immutable collections store a data set that never changes. However, you may be able to take advantage of the performance and space-saving benefits even if your data is subject to change. These collections may provide better performance than the mutable collections, even if your data changes occasionally.
If you have a large number of values, you may consider storing them in a HashMap. If you are constantly adding and removing entries, then this is a good choice. But, if you have a set of values that never change, or rarely change, and you read from that set a lot, then the immutable Map is a more efficient choice. If the data set is read frequently, and the values change only rarely, then you may find that the overall speed is faster, even when you include the performance impact of destroying and rebuilding an immutable Map when a value changes.
Also note that depending on the type of keys and the actual data (specifically on the number of collisions), an unmodifiable map might perform worse than a HashMap because contrary to HashMap unmodifiable map is incapable to store and reuse hashes of the keys and hence might perform more comparisons of keys using equals method. And in case of large number of collisions, unmodifiable map has no means to mitigate performance degradation, unlike HashMap which since would try to arrange nodes into a tree when the number of nodes in a bucket exceeds certain threshold.
Spiking of other alternatives doubly-brace initialization, which creates an instance of anonymous inner class, is indisputably a discouraged practice.
Static and instance initializer blocks have no downsides from the technical point of view, they allow you to keep the variables final if you need that. But some might argue that from the perspective of clean coding, initializer blocks are not very great.
Another possibility you might consider is to initialize a map inline using Stream API (for instance, when map-data can be expressed as a sequence that is easy to generate).