I'm still getting used to Java Generics however I'm currently in the process of updating an application written prior to generics to use the latest version of java.
Problem is the code was not written with type safety in mind!
We have a whole bunch of Maps that basically hold various object types including strings. For example:
Map map = new HashMap();
map.put("key1", "String1");
map.put("key2", new Date());
map.put("key3", new CutsomClass());
Now I'm still struggling with the best way to handle these without getting into refactoring a whole lot of code. Refactoring is not an option at this time.
Currently I can't see anything past Map<String, Object> although Map<String, ? super Object> works but I think its essentially the same thing ?
I'm still struggling with the best way to handle these without getting
into refactoring a whole lot of code
So don't change them at all. The raw types - that is, the non-generic types - are still technically valid. It's not ideal and it will generate a compiler warning but the code will work (well, work as well as it ever did).
All classes extend Object so you can put any value you want into the following map:
Map<String, Object> map = new HashMap<>();
You get an additional guarantee that the key is a string, so its somewhat better than using the raw type.
Basically though, you should really try to avoid using a map if you can't define the type of the key or the value.
As of now, you can only replace the raw type Map with Map<String, Object>; but that type information is close to "useless". Unless you refactor your whole component to deal with different map objects, there isn't much you can do. Of course, you can get rid of the type warnings, but you still have to do instanceof checks each time you access a Map value.
On way out of this: assuming that number of "value" types is known and reasonably small, you could create a bunch of helper methods that go like:
public Map<String, Date> extractDates(Map<String, Object> allValues) {
...
This method could implement a "wrapper" around the allValues map that only provides those map entries that are actually Date objects.
Meaning: you keep your current map object, but you provide "more specialized" views on that map. That allows you to write new code exploiting the additional type information.
But of course, this doesn't come for free. It adds certain complexity, and defining the exact details of such "view maps" might turn out to be rather complicated.
There is little you can do to achieve the full static type checking available with Generics used properly. However, I don't believe you must abandon type checking completely and rely on run-time casting in this case. I think you can go one step towards that.
I assume this is a common map that the code uses for general storage, perhaps for persistence or properties etc. If this is the case then you can at least do something like this:
class AnyMap<K> {
final Map<K,Object> map;
public AnyMap(Map<K, Object> map) {
this.map = map;
}
public <V> Map<K,V> as(Class<V> theClass) {
return (Map<K,V>) map;
}
}
public void test() {
AnyMap<String> commonMap = new AnyMap<>(Collections.EMPTY_MAP);
// Use this one as a Date map.
Map<String,Date> dateMap = commonMap.as(Date.class);
// This one as a String map.
Map<String,String> stringMap = commonMap.as(String.class);
}
This is a kind of Map holder that can then deliver the map as a proper generic object with the correct bounds. Hopefully you will find that certain modules will use the common map exclusively for Dates and others will use Strings. In areas such as these you can use Map<X,Y> as(...) to give you a properly statically-checked map for that module/section and use that exclusively in that code section.
As Michael suggested Map<String, Object> map = new HashMap<>();
is a first step.
However, it assumes that you have only String as keys and you will probably need to cast objects you get from the map.
I think that a second step would be to split this map into multiple maps declaring a more specific type :
Map<String, Date> mapDates = new HashMap<>();
Map<String, String> mapStrings = new HashMap<>();
Map<String, CustomClass> mapCutsomClasses = new HashMap<>();
Dont use raw types... see this to know why..
Now, you can break it out as following, so you can get it:
your map<K,V> has as keys Strings only, so K = string will be correct, on the other hand "String1", new Date() and new Custom Class seems to have nothing in common, but wait, all the classes in java are actually inheriting the Object class... that means you can do V=Object
now your map can be declared as Map<String, Object> and all this
map.put("key1", "String1");
map.put("key2", new Date());
map.put("key3", new CutsomClass());
will be ok
Conversion from non-generics (e.g. old Java) to generics can be a real PITA. The easiest way to do it is to replace each Map, Set, List with related generic, e.g.
Map<String, Object> map = new HashMap<>();
but only if mapped objects are not generics too (or are generics just used for reading). E.g. if in your code had something like
Map hasmap = new HashMap();
hashmap.put("blah",123)
map.put("keyX",hashmap);
In such cases, when you find map getter, and you will have in original code a put, you will have lot of troubles:
Map hashmap = (HashMap)(map.get("keyX"));
Integer value = hashmap.get("blah");
hashmap.put("otherkey","mooo");
In this case, you can't have clear code: if you use question marks, you will face errors, like in
Map<?,?> hashmap = (HashMap<?,?>)(map.get("keyX"));
Integer value = (Integer)hashmap.get("blah"); // this works
hashmap.put("otherkey","mooo"); // this crashes
so you have two alternatives: rewrite code (to avoid warnings), or force everything at Objects, and receive Unchecked Casts warnings.
#SuppressWarnings("unchecked")
Map<Object,Object> hashmap = (HashMap<Object,Object>)(map.get("keyX"));
Integer value = (Integer)hashmap.get("blah"); // this works
hashmap.put("otherkey","mooo"); // this works too
Further details about question marks and generics can be found here: What is the difference between ? and Object in Java generics?
Looks like you want to quickly port the old code in and also you want to move the old code towards strict type safety without refactoring a large code base. Keep your old code by porting it in using this:
Map<String, Object> oldMapPorted = new HashMap<>();
New code written in this app can use a technique like this for strict type safety:
Map<String, Date> newMapDates = new HashMap<>();
Map<String, String> newMapStrings = new HashMap<>();
Map<String, CustomClass> newCutsomClasses = new HashMap<>();
A new class can be created for future edits and enhancements while the old code still has the same potential instability as usual.
Related
I was experimenting with various ways of instantiating a HashMap and there's a problem I've been facing.
One would be able to choose between the following, when creating a map from String to String:
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map = new HashMap<>();
Map<String, String> map = new HashMap();
The benefit of using the first one, I thought, would be to bind the overall object in memory to the String type for both key and value, since the reference(map) would point to the actual object of type HashMap<String, String>
Running the following code throws no error and displays reasonable output:
Map<String, String> map = new HashMap<String, String>();
map.put("hi", "there");
Map map2 = map;
map2.put(5, 4);
System.out.println(map.get("hi"));
System.out.println(map2.get("hi"));
System.out.println(map2.get(5));
System.out.println(map2.get(5).getClass());
System.out.println(map.size());
the output is:
there
there
4
class java.lang.Integer
2
We can clearly see that both references point to the same object in memory, which I thought would be of type HashMap<String, String>, but we can add any subtype of Object, when the reference is not parameterized.
My question is, since the parameterized constructor does not matter for the object stored in memory, why would we ever want to repeat the types on the right side?
Why write this:
Map<String, String> map = new HashMap<String, String>();
instad of this:
Map<String, String> map = new HashMap();
even if it has the same effect?
Due to type erasure, the generated code doesn’t differ. All that Generics is about, is to get compiler feedback about the correctness of the code. Only when your code is free of “unchecked” and “raw types” warnings, you can be sure that no “heap pollution”, i.e. stored objects of incorrect type, will occur.
In this regard, the line
Map<String, String> map = new HashMap();
is problematic because it generates compiler warnings. Of course, when you look at it and have enough understanding about he implications, you will know that it can’t cause any harm.
In contrast, the similar line
Map<String, String> map = new HashMap(oldMap);
does allow to initialize the map with incorrect contents. Of course, you could now check the type of oldMap, to see that it is compatible, and be again confident that this specific case is ok. But this approach defeats the entire purpose of Generics, to let the compiler check this for you, which you can easily achieve by inserting the <>.
In general, when you have a large codebase, you want to have it entirely warning free, so you do not have to go at each location, to see whether it can be considered harmless, even if it only requires a short look.
The necessity to insert <> is connected to the history. When Java 5 was released, the default for all method invocations was to perform the generic check, so it’s not convincing to say that “compatibility concerns” were the reason to make raw type the default for constructor invocations. My guess is, the javac developers had difficulties with the implementation of the type inference and there was release time pressure.
Note that with Java 5 and Java 6, the developer even had to insert the full type explicitly for each generic instantiation.
With Java 7, the problem was mitigated by introducing the “diamond operator”, <>, to let the compiler infer the generic types, as it should have done in the first place. It’s still necessary to request the inference explicitly, though, for “compatibility”, despite, as said, for method invocations, there is no opt-in mechanism and it works since day one of Generics. To me, the raw type default is a design mistake.
But still, for practical purposes, as said above, you want a warning free application with the compiler performing the checks, hence, you should always insert the necessary <>.
Since Java 10, you can declare local variables like
var map = new HashMap<String, String>();
eliminating all redundancy in the declaration.
What is the difference between the following maps I create (in another question, people answered using them seemingly interchangeably and I'm wondering if/how they are different):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
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.
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
Map has the following implementations:
HashMap Map m = new HashMap();
LinkedHashMap Map m = new LinkedHashMap();
Tree Map Map m = new TreeMap();
WeakHashMap Map m = new WeakHashMap();
Suppose you have created one method (this is just pseudocode).
public void HashMap getMap(){
return map;
}
Suppose your project requirements change:
The method should return map contents - Need to return HashMap.
The method should return map key's in insertion order - Need to change return type HashMap to LinkedHashMap.
The method should return map key's in sorted order - Need to change return type LinkedHashMap to TreeMap.
If your method returns specific classes instead of something that implements the Map interface, you have to change the return type of getMap() method each time.
But if you use the polymorphism feature of Java, and instead of returning specific classes, use the interface Map, it improves code reusability and reduces the impact of requirement changes.
I was just going to do this as a comment on the accepted answer but it got too funky (I hate not having line breaks)
ah, so the difference is that in
general, Map has certain methods
associated with it. but there are
different ways or creating a map, such
as a HashMap, and these different ways
provide unique methods that not all
maps have.
Exactly--and you always want to use the most general interface you possibly can. Consider ArrayList vs LinkedList. Huge difference in how you use them, but if you use "List" you can switch between them readily.
In fact, you can replace the right-hand side of the initializer with a more dynamic statement. how about something like this:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
This way if you are going to fill in the collection with an insertion sort, you would use a linked list (an insertion sort into an array list is criminal.) But if you don't need to keep it sorted and are just appending, you use an ArrayList (More efficient for other operations).
This is a pretty big stretch here because collections aren't the best example, but in OO design one of the most important concepts is using the interface facade to access different objects with the exact same code.
Edit responding to comment:
As for your map comment below, Yes using the "Map" interface restricts you to only those methods unless you cast the collection back from Map to HashMap (which COMPLETELY defeats the purpose).
Often what you will do is create an object and fill it in using it's specific type (HashMap), in some kind of "create" or "initialize" method, but that method will return a "Map" that doesn't need to be manipulated as a HashMap any more.
If you ever have to cast by the way, you are probably using the wrong interface or your code isn't structured well enough. Note that it is acceptable to have one section of your code treat it as a "HashMap" while the other treats it as a "Map", but this should flow "down". so that you are never casting.
Also notice the semi-neat aspect of roles indicated by interfaces. A LinkedList makes a good stack or queue, an ArrayList makes a good stack but a horrific queue (again, a remove would cause a shift of the entire list) so LinkedList implements the Queue interface, ArrayList does not.
As noted by TJ Crowder and Adamski, one reference is to an interface, the other to a specific implementation of the interface. According to Joshua Block, you should always attempt to code to interfaces, to allow you to better handle changes to underlying implementation - i.e. if HashMap suddenly was not ideal for your solution and you needed to change the map implementation, you could still use the Map interface, and change the instantiation type.
Map is the static type of map, while HashMap is the dynamic type of map. This means that the compiler will treat your map object as being one of type Map, even though at runtime, it may point to any subtype of it.
This practice of programming against interfaces instead of implementations has the added benefit of remaining flexible: You can for instance replace the dynamic type of map at runtime, as long as it is a subtype of Map (e.g. LinkedHashMap), and change the map's behavior on the fly.
A good rule of thumb is to remain as abstract as possible on the API level: If for instance a method you are programming must work on maps, then it's sufficient to declare a parameter as Map instead of the stricter (because less abstract) HashMap type. That way, the consumer of your API can be flexible about what kind of Map implementation they want to pass to your method.
In your second example the "map" reference is of type Map, which is an interface implemented by HashMap (and other types of Map). This interface is a contract saying that the object maps keys to values and supports various operations (e.g. put, get). It says nothing about the implementation of the Map (in this case a HashMap).
The second approach is generally preferred as you typically wouldn't want to expose the specific map implementation to methods using the Map or via an API definition.
Adding to the top voted answer and many ones above stressing the "more generic, better", I would like to dig a little bit more.
Map is the structure contract while HashMap is an implementation providing its own methods to deal with different real problems: how to calculate index, what is the capacity and how to increment it, how to insert, how to keep the index unique, etc.
Let's look into the source code:
In Map we have the method of containsKey(Object key):
boolean containsKey(Object key);
JavaDoc:
boolean java.util.Map.containsValue(Object value)
Returns true if this map maps one or more keys to the specified value. More formally, returns true if and only if this map contains at least one mapping to a value v such that (value==null ? v==null : value.equals(v)). This operation will probably require time linear in the map size for most implementations of the Map interface.
Parameters:value
value whose presence in this map is to betested
Returns:true
if this map maps one or more keys to the specified
valueThrows:
ClassCastException - if the value is of an inappropriate type for this map (optional)
NullPointerException - if the specified value is null and this map does not permit null values (optional)
It requires its implementations to implement it, but the "how to" is at its freedom, only to ensure it returns correct.
In HashMap:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
It turns out that HashMap uses hashcode to test if this map contains the key. So it has the benefit of hash algorithm.
You create the same maps.
But you can fill the difference when you will use it. With first case you'll be able to use special HashMap methods (but I don't remember anyone realy useful), and you'll be able to pass it as a HashMap parameter:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Map is interface and Hashmap is a class that implements Map Interface
Map is the Interface and Hashmap is the class that implements that.
So in this implementation you create the same objects
HashMap is an implementation of Map so it's quite the same but has "clone()" method as i see in reference guide))
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
First of all Map is an interface it has different implementation like - HashMap, TreeHashMap, LinkedHashMap etc. Interface works like a super class for the implementing class. So according to OOP's rule any concrete class that implements Map is a Map also. That means we can assign/put any HashMap type variable to a Map type variable without any type of casting.
In this case we can assign map1 to map2 without any casting or any losing of data -
map2 = map1
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.
What is the difference between the following maps I create (in another question, people answered using them seemingly interchangeably and I'm wondering if/how they are different):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
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.
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
Map has the following implementations:
HashMap Map m = new HashMap();
LinkedHashMap Map m = new LinkedHashMap();
Tree Map Map m = new TreeMap();
WeakHashMap Map m = new WeakHashMap();
Suppose you have created one method (this is just pseudocode).
public void HashMap getMap(){
return map;
}
Suppose your project requirements change:
The method should return map contents - Need to return HashMap.
The method should return map key's in insertion order - Need to change return type HashMap to LinkedHashMap.
The method should return map key's in sorted order - Need to change return type LinkedHashMap to TreeMap.
If your method returns specific classes instead of something that implements the Map interface, you have to change the return type of getMap() method each time.
But if you use the polymorphism feature of Java, and instead of returning specific classes, use the interface Map, it improves code reusability and reduces the impact of requirement changes.
I was just going to do this as a comment on the accepted answer but it got too funky (I hate not having line breaks)
ah, so the difference is that in
general, Map has certain methods
associated with it. but there are
different ways or creating a map, such
as a HashMap, and these different ways
provide unique methods that not all
maps have.
Exactly--and you always want to use the most general interface you possibly can. Consider ArrayList vs LinkedList. Huge difference in how you use them, but if you use "List" you can switch between them readily.
In fact, you can replace the right-hand side of the initializer with a more dynamic statement. how about something like this:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
This way if you are going to fill in the collection with an insertion sort, you would use a linked list (an insertion sort into an array list is criminal.) But if you don't need to keep it sorted and are just appending, you use an ArrayList (More efficient for other operations).
This is a pretty big stretch here because collections aren't the best example, but in OO design one of the most important concepts is using the interface facade to access different objects with the exact same code.
Edit responding to comment:
As for your map comment below, Yes using the "Map" interface restricts you to only those methods unless you cast the collection back from Map to HashMap (which COMPLETELY defeats the purpose).
Often what you will do is create an object and fill it in using it's specific type (HashMap), in some kind of "create" or "initialize" method, but that method will return a "Map" that doesn't need to be manipulated as a HashMap any more.
If you ever have to cast by the way, you are probably using the wrong interface or your code isn't structured well enough. Note that it is acceptable to have one section of your code treat it as a "HashMap" while the other treats it as a "Map", but this should flow "down". so that you are never casting.
Also notice the semi-neat aspect of roles indicated by interfaces. A LinkedList makes a good stack or queue, an ArrayList makes a good stack but a horrific queue (again, a remove would cause a shift of the entire list) so LinkedList implements the Queue interface, ArrayList does not.
As noted by TJ Crowder and Adamski, one reference is to an interface, the other to a specific implementation of the interface. According to Joshua Block, you should always attempt to code to interfaces, to allow you to better handle changes to underlying implementation - i.e. if HashMap suddenly was not ideal for your solution and you needed to change the map implementation, you could still use the Map interface, and change the instantiation type.
Map is the static type of map, while HashMap is the dynamic type of map. This means that the compiler will treat your map object as being one of type Map, even though at runtime, it may point to any subtype of it.
This practice of programming against interfaces instead of implementations has the added benefit of remaining flexible: You can for instance replace the dynamic type of map at runtime, as long as it is a subtype of Map (e.g. LinkedHashMap), and change the map's behavior on the fly.
A good rule of thumb is to remain as abstract as possible on the API level: If for instance a method you are programming must work on maps, then it's sufficient to declare a parameter as Map instead of the stricter (because less abstract) HashMap type. That way, the consumer of your API can be flexible about what kind of Map implementation they want to pass to your method.
In your second example the "map" reference is of type Map, which is an interface implemented by HashMap (and other types of Map). This interface is a contract saying that the object maps keys to values and supports various operations (e.g. put, get). It says nothing about the implementation of the Map (in this case a HashMap).
The second approach is generally preferred as you typically wouldn't want to expose the specific map implementation to methods using the Map or via an API definition.
Adding to the top voted answer and many ones above stressing the "more generic, better", I would like to dig a little bit more.
Map is the structure contract while HashMap is an implementation providing its own methods to deal with different real problems: how to calculate index, what is the capacity and how to increment it, how to insert, how to keep the index unique, etc.
Let's look into the source code:
In Map we have the method of containsKey(Object key):
boolean containsKey(Object key);
JavaDoc:
boolean java.util.Map.containsValue(Object value)
Returns true if this map maps one or more keys to the specified value. More formally, returns true if and only if this map contains at least one mapping to a value v such that (value==null ? v==null : value.equals(v)). This operation will probably require time linear in the map size for most implementations of the Map interface.
Parameters:value
value whose presence in this map is to betested
Returns:true
if this map maps one or more keys to the specified
valueThrows:
ClassCastException - if the value is of an inappropriate type for this map (optional)
NullPointerException - if the specified value is null and this map does not permit null values (optional)
It requires its implementations to implement it, but the "how to" is at its freedom, only to ensure it returns correct.
In HashMap:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
It turns out that HashMap uses hashcode to test if this map contains the key. So it has the benefit of hash algorithm.
You create the same maps.
But you can fill the difference when you will use it. With first case you'll be able to use special HashMap methods (but I don't remember anyone realy useful), and you'll be able to pass it as a HashMap parameter:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Map is interface and Hashmap is a class that implements Map Interface
Map is the Interface and Hashmap is the class that implements that.
So in this implementation you create the same objects
HashMap is an implementation of Map so it's quite the same but has "clone()" method as i see in reference guide))
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
First of all Map is an interface it has different implementation like - HashMap, TreeHashMap, LinkedHashMap etc. Interface works like a super class for the implementing class. So according to OOP's rule any concrete class that implements Map is a Map also. That means we can assign/put any HashMap type variable to a Map type variable without any type of casting.
In this case we can assign map1 to map2 without any casting or any losing of data -
map2 = map1
So I have two questions about HashMaps in Java:
What is the correct way to initialize a HashMap? I think it might be best in my situation to use:
HashMap x = new HashMap();
But Eclipse keeps suggesting that I use:
HashMap<something, something> map = new HashMap();
Which is better?
Can a HashMap hold different types of objects/data types as values? For example, would this work and be OK:
map.put("one", 1);
map.put("two", {1, 2});
map.put("three", "hello");
In the first put(), I want an int as a value, in the second an int[], and third a string. Is this okay to do in Java with HashMaps? Also, is it okay to store a HashMap as a value within a HashMap?
It really depends on what kind of type safety you need. The non-generic way of doing it is best done as:
Map x = new HashMap();
Note that x is typed as a Map. this makes it much easier to change implementations (to a TreeMap or a LinkedHashMap) in the future.
You can use generics to ensure a certain level of type safety:
Map<String, Object> x = new HashMap<String, Object>();
In Java 7 and later you can do
Map<String, Object> x = new HashMap<>();
The above, while more verbose, avoids compiler warnings. In this case the content of the HashMap can be any Object, so that can be Integer, int[], etc. which is what you are doing.
If you are still using Java 6, Guava Libraries (although it is easy enough to do yourself) has a method called newHashMap() which avoids the need to duplicate the generic typing information when you do a new. It infers the type from the variable declaration (this is a Java feature not available on constructors prior to Java 7).
By the way, when you add an int or other primitive, Java is autoboxing it. That means that the code is equivalent to:
x.put("one", Integer.valueOf(1));
You can certainly put a HashMap as a value in another HashMap, but I think there are issues if you do it recursively (that is put the HashMap as a value in itself).
This is a change made with Java 1.5. What you list first is the old way, the second is the new way.
By using HashMap you can do things like:
HashMap<String, Doohickey> ourMap = new HashMap<String, Doohickey>();
....
Doohickey result = ourMap.get("bob");
If you didn't have the types on the map, you'd have to do this:
Doohickey result = (Doohickey) ourMap.get("bob");
It's really very useful. It helps you catch bugs and avoid writing all sorts of extra casts. It was one of my favorite features of 1.5 (and newer).
You can still put multiple things in the map, just specify it as Map, then you can put any object in (a String, another Map, and Integer, and three MyObjects if you are so inclined).
Eclipse is recommending that you declare the type of the HashMap because that enforces some type safety. Of course, it sounds like you're trying to avoid type safety from your second part.
If you want to do the latter, try declaring map as HashMap<String,Object>.
The way you're writing it is equivalent to
HashMap<Object, Object> map = new HashMap<Object, Object>();
What goes inside the brackets is you communicating to the compiler what you're going to put in the HashMap so that it can do error checking for you. If Object, Object is what you actually want (probably not) you should explicitly declare it. In general you should be as explicit as you can with the declaration to facilitate error checking by the compiler. What you've described should probably be declared like this:
HashMap<String, Object> map = new HashMap<String, Object>();
That way you at least declare that your keys are going to be strings, but your values can be anything. Just remember to use a cast when you get a value back out.
The 2nd one is using generics which came in with Java 1.5. It will reduce the number of casts in your code & can help you catch errors at compiletime instead of runtime. That said, it depends on what you are coding. A quick & dirty map to hold a few objects of various types doesn't need generics. But if the map is holding objects all descending from a type other than Object, it can be worth it.
The prior poster is incorrect about the array in a map. An array is actually an object, so it is a valid value.
Map<String,Object> map = new HashMap<String,Object>();
map.put("one",1); // autoboxed to an object
map.put("two", new int[]{1,2} ); // array of ints is an object
map.put("three","hello"); // string is an object
Also, since HashMap is an object, it can also be a value in a HashMap.
A HashMap can hold any object as a value, even if it is another HashMap. Eclipse is suggesting that you declare the types because that is the recommended practice for Collections. under Java 5. You are free to ignore Eclipse's suggestions.
Under Java 5, an int (or any primitive type) will be autoboxed into an Integer (or other corresponding type) when you add it to a collection. Be careful with this though, as there are some catches to using autoboxing.
Eclipse is suggesting you to define generic type so that you can have type safety. You can write
Map m = new HashMap();
which does not ensure type safety but following will ensure type safety
Map<Object,Object> = new HashMap<Object,Object>();
The Object can be any type such as String, Integer etc.
Map.of literals
As of Java 9, there is yet another way to instantiate a Map. You can create an unmodifiable map from zero, one, or several pairs of objects in a single-line of code. This is quite convenient in many situations.
For an empty Map that cannot be modified, call Map.of(). Why would you want an empty set that cannot be changed? One common case is to avoid returning a NULL where you have no valid content.
For a single key-value pair, call Map.of( myKey , myValue ). For example, Map.of( "favorite_color" , "purple" ).
For multiple key-value pairs, use a series of key-value pairs. ``Map.of( "favorite_foreground_color" , "purple" , "favorite_background_color" , "cream" )`.
If those pairs are difficult to read, you may want to use Map.of and pass Map.Entry objects.
Note that we get back an object of the Map interface. We do not know the underlying concrete class used to make our object. Indeed, the Java team is free to used different concrete classes for different data, or to vary the class in future releases of Java.
The rules discussed in other Answers still apply here, with regard to type-safety. You declare your intended types, and your passed objects must comply. If you want values of various types, use Object.
Map< String , Color > preferences = Map.of( "favorite_color" , Color.BLUE ) ;
In answer to your second question: Yes a HashMap can hold different types of objects. Whether that's a good idea or not depends on the problem you're trying to solve.
That said, your example won't work. The int value is not an Object. You have to use the Integer wrapper class to store an int value in a HashMap