What is the difference between the HashMap and Map objects in Java?

321,170

Solution 1

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.

Solution 2

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

Solution 3

enter image description here

Map has the following implementations:

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Tree Map Map m = new TreeMap();

  4. 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:

  1. The method should return map contents - Need to return HashMap.
  2. The method should return map key's in insertion order - Need to change return type HashMap to LinkedHashMap.
  3. 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.

Solution 4

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.

Solution 5

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.

Share:
321,170
Tony Stark
Author by

Tony Stark

Updated on February 16, 2020

Comments

  • Tony Stark
    Tony Stark over 4 years

    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>();
    
  • Tony Stark
    Tony Stark almost 15 years
    but in this example, i only get the methods from the general List class, right? regardless of whether I make it a LinkedList() or an ArrayList()? it's just that if I use insertion sort (which I imagine must be a method for List that LinkedList and ArrayList get by inheritance) it works way faster on the LinkedList?
  • Tony Stark
    Tony Stark almost 15 years
    i guess what i'm looking for is whether or not when I say Map<string, string> m = new HashMap<string, string>() my Map m can use the methods specific to HashMap, or not. I'm thinking it can't?
  • Tony Stark
    Tony Stark almost 15 years
    ah, wait, no, my Map m from above must have the methods from HashMap.
  • Tony Stark
    Tony Stark almost 15 years
    so basically the only perk of using Map in the 'interface sense' is that if i have a method that requires a map, i'm guaranteeing any type of map will work in this method. but if i used a hashmap, i'm saying the method only works with hashmaps. or, put another way, my method only uses methods defined in Map class but inherited by the other Classes which extend Map.
  • Tony Stark
    Tony Stark almost 15 years
    in addition to the perk you mentioned above, where using List means I don't need to decide which type of List I want until runtime, whereas if the interface thing didn't exist I'd have to pick one before compiling and running
  • Bill K
    Bill K almost 15 years
    I think you are correct in all your comments--exactly correct actually.
  • Bill K
    Bill K almost 15 years
    And "Insertion Sort" isn't a method, it's a way of adding new records into the correctly sorted order. If you do that in an ArrayList, it has to block-move all the records past the one you are inserting down one for every single record added--a LinkedList, however, can just take a mid-collection insert extremely quickly due to it's nature.
  • Tony Stark
    Tony Stark almost 15 years
    "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" isn't true right? Map<String, Object> map = new HashMap<String, Object>(); creates a Map map which has a HashMap under the hood, so I can use the HashMap methods. In a case where I will only EVER use HashMap methods, there is no difference between using Map or HashMap on the left side of the equals sign. Using Map is only beneficial where any type of Map can be passed into the method or I have a run-time choice, or something of the like
  • Bill K
    Bill K almost 15 years
    Even if a map is a HashMap under the hood, it's still a "Map" until you cast it--you cannot access any HashMap methods without casting. Sometimes I visualize an interface as a shield with a bunch of holes in it that "Covers Up" the objects personality only letting some pieces through.
  • OneWorld
    OneWorld over 13 years
    I assume: first = HashMap<String, Object> map = new HashMap<String, Object>();
  • Gerard
    Gerard almost 9 years
    It is similar to how often a List is implemented as an ArrayList
  • Collin Fox
    Collin Fox almost 3 years
    but why can't we just have 1 single type of map that we create the same way with all the functions already available???? why do we need 3 types of lists (ArrayList, List, and Collection), Long vs long, int vs Integer? With top answers as bad and overly complicated as the one in this post it's clear no one really knows why they exist
  • Collin Fox
    Collin Fox almost 3 years
    Why is there not just 1 single type of map that we create the same way with all the functions already available? Wouldn't this make more sense than overly complicating objects for minimal benefit?
  • T.J. Crowder
    T.J. Crowder almost 3 years
    @CollinFox - Do you have just one kind of kitchen knife? :-) It's normal to have an interface and multiple impls of it that offer different features. Compare the descriptions of HashMap, TreeMap, and LinkedHashMap. You can see that they offer different kinds of runtime perf, ordering guarantees, etc. for diff situations.
  • Bill K
    Bill K almost 3 years
    @CollinFox it’s worth figuring out, we ask that exact question on our interviews. It’sa good way to tell the difference between someone who understands programming and someone who learned by copy/paste from slash dot. It’s good that you are asking! What do you do if you need your hash to be in the order keys are added? What if you need it to be as fast as possible? What if you need it in alphabetical order? Those three use cases require completely different implementations. How would you solve those three problems in a simpler way?
  • Collin Fox
    Collin Fox almost 3 years
    Questions like those three you mentioned are what I was hoping would have been simply put by this stack overflow post - what are the answers to such questions?