I am doing a small project of reading delimited files into columns and displays them. Now i want a column class that is basically a wrapper arround a HashMap, but there is one problem, I don't know the second generic data type until the Columns constructor is called out. The constructor takes an enum with data type argument, like the below:
private enum DataTypes{
NONE, INTEGER, STRING, DATE
}
what I have in mind is create the Column class field as a Collection, then I could initialize it when the constructor is called, like the below:
private class Column {
Collection values;
public Column(DataTypes type){
determineType(type);
}
private void determineType(DataTypes type){
switch(type){
case NONE: this.values = (Collection) new HashMap<Integer, StringBuilder>();
break;
case INTEGER: this.values = (Collection) new HashMap<Integer, Integer>();
break;
case STRING: this.values = (Collection) new HashMap<Integer, StringBuilder>();
break;
case DATE: this.values = (Collection) new HashMap<Integer, Date>();
break;
}
}
}
My question is why do I have to cast it to the collection type (as eclipse force me) and does it mean that I cannot use any methods of HashMap?
Also is there a better aproach or maybe design pattern for a situation like that, and what could be improved? Is there anything that could go wrong with this apraoch later on like for example when adding a value as I feel that I would need to have an overloaded function for each data type to add a value.
Sorry for such a long list of questions, but I just got little experience in actuall projects and design and need a "little" guidance. (University projects are small with variables like a,b,c,x,y etc)
Thanks:)
EDIT
After a whole day of searching and asking this very badly written question I have found the Strategy design pattern is what I need, i will post the code later.
HashMap doesn't extend Collection. So you can't assign a HashMap to a variable of type Collection.
Eclipse doesn't force you to cast. You chose to accept one of the ways it suggested to make that code compile, but that doesn't mean the code is correct. It will fail at runtime, since HashMap is not a Collection.
I have a hard time understanding what this Column should represent. What would be the keys and the values of that HashMap? And, if what you actually want is not a HashMap, but a Collection, what woud be the values of the Collection?
In any case, since all the types you want to store in the Collection/Map have a single ancestor class (Object), you should probably use that as the type of your Collection/Map. You should also avoir storing StringBuilder. Use String instead.
You can declare your HashMap with the wildcard operator.
private class Column {
HashMap<?,?> values;
Then you can initilize the Map as normal
case INTEGER: this.values = new HashMap<Integer, Integer>();
Only problem is that you have to cast your HashMap to HashMap<Integer, Integer> every time you put something to it.
HashMap is not a Collection nor does it inherit from Collection
The class definition of HashMap is:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
Therefore, HashMap is a Map and implement the Map interface, not Collection
Because all of your case statements are instantiating HashMap, it makes sense to change the declaration of values to a Map instead of `Collection:
Map values;
In addition, because all of your case statements declare HashMap with the key as type Integer, but the values as varying types, you can declare the values Map accordingly.
Map<Integer, ?> values;
The reason Eclipse was suggesting you cast your HashMap to Collection is because the Maps are not descendants of Collection and therefore the only way to satisfy the compiler would be to cast them, which would still end up with a runtime error, since they are not collections.
By making the change to the declaration of values to type Map, you will not only no longer need to cast, but will eliminate your runtime errors.
Related
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
I am currently reading "Core Java for the Impatient" by Horstmann (I recommend it, love the concise style) and I have a trouble understanding one of the exercises pertaining to the collection API. The exercise is as follows:
I encouraged you to use interfaces instead of concrete data structures, for example, a
Map instead of a TreeMap. Unfortunately, that advice goes only so far. Why can’t
you use a Map<String, Set<Integer>> to represent a table of contents?
(Hint: How would you initialize it?) What type can you use instead?
The following code compiles and works without a problem though, even though the interfaces were used for variable declarations. What am I missing?
Map<String, Set<Integer>> toc = new HashMap<>();
toc.put("element1", IntStream.of(1, 2, 3).boxed().collect(Collectors.toSet()));
toc.put("element2", IntStream.of (3, 4, 7).boxed().collect(Collectors.toSet()));
toc.forEach( (k, v) -> {
System.out.print(k + " ");
v.forEach(val -> System.out.print(val + " "));
System.out.println();
} );
}
An interface like Map is a supertype for all interfaces that inherit it and all classes that implement it. So TreeMap inherits from Map, and because you can always assign to a variable any reference that is of a subtype, it's perfectly acceptable to assign a TreeMap reference to a Map variable. This is called a widening reference conversion
https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.5
"Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time."
So, yes, you certainly can use a Map<String, Set<Integer>> to represent something in your domain model, but you can't instantiate an interface directly; you must instantiate a concrete type (class) that implements it. This is exactly what you did when you declared
Map<String, Set<Integer>> toc = new HashMap<>();
As an extension of this principle, you could just as easily write
AbstractMap<String, Set<Integer>> toc = new HashMap<>();
since AbstractMap is also a supertype of HashMap.
In general you want to declare the widest type for the variable that can hold the largest possible set of subtype references that work in your logic. If you need a sorted map, then 'Map' is too wide; it doesn't enforce sortedness. You'd have to declare the variable as TreeMap , or better, SortedMap for that.
Usually the interface is the widest applicable type, but you have to think about it in case it's not.
EDIT: Mentioned SortedMap in light of comment.
I have got in touch with book's author and he agreed that question was unclear and it was to lead the reader to use wildcard types. The exercise in question was changed to:
Suppose you have a method parameter of type Map<String, Set<Integer>>, and someone calls your method with a HashMap<String, HashSet<Integer>>. What happens? What parameter type can you use instead?
The answer is that in this case one should use the wildcard type: Map<String, ? extends Set<Integer>>.
What is the difference between the following?
new HashMap(); vs new HashMap<Integer, String>();?
I've never used the former or seen it but is there any differences I should know?
First one doesnt know what type of data hashmap has. You can have any type of object in this map. Second one specifies what is key and value types for this map. You can only insert those types into map. This approach is called generics.
Following statement will creates a map whose key-value types are raw. (java.lang.Object).
HashMap map=new HashMap();
In second statement, you are specifying type of Key and Value (Read Java Generics) .
HashMap<Integer,String> map=new HashMap<Integer,String>();
It should be new Hashmap<Integer, String>().
The first case will use Object type for keys as well as values and will return Object instances when you retrieve them. In the second case, you are specifying that the keys should be of Integer type and the values are of String type. So, you should add accordingly and when you retrieve, you'll get String object for values and Integer object for keys and you will not need a cast as in the previous case.
P.S. I think the reason you should use Integer and not int is that the type used should be "nullable" if some method needs to return null. int is not nullable.
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