Quelle est la différence entre les objets HashMap et Map en Java?


Quelle est la différence entre les cartes suivantes que je crée (dans une autre question, les gens ont répondu en les utilisant apparemment de manière interchangeable et je me demande si / en quoi elles sont différentes):

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
Author: 030, 2009-08-28

13 answers

Il n'y a pas de différence entre les objets; vous avez un HashMap<String, Object> dans les deux cas. Il y a une différence dans l'interface que vous avez à l'objet. Dans le premier cas, l'interface est HashMap<String, Object>, tandis que dans le second c'est Map<String, Object>. Mais l'objet sous-jacent est le même.

L'avantage d'utiliser Map<String, Object> est que vous pouvez changer l'objet sous-jacent pour qu'il soit un autre type de carte sans rompre votre contrat avec un code qui l'utilise. Si vous le déclarez comme HashMap<String, Object> , vous devez modifiez votre contrat si vous souhaitez modifier l'implémentation sous-jacente.


Exemple: disons que j'écris cette classe:

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...
}

La classe a quelques cartes internes de string->object qu'elle partage (via des méthodes d'accesseur) avec des sous-classes. Disons que je l'écris avec HashMap s pour commencer parce que je pense que c'est la structure appropriée à utiliser lors de l'écriture de la classe.

Plus tard, Mary écrit le code en le sous-classant. Elle a quelque chose à faire avec les deux things et moreThings, donc naturellement elle met cela dans une méthode commune, et elle utilise le même type que j'ai utilisé sur getThings/getMoreThings lors de la définition de sa méthode:

class SpecialFoo extends Foo {
    private void doSomething(HashMap<String, Object> t) {
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }

    // ...more...
}

Plus Tard, je décide qu'en fait, c'est mieux si j'utilise TreeMap au lieu de HashMap dans Foo. Je mets à jour Foo, en changeant HashMap en TreeMap. Maintenant, SpecialFoo ne compile plus, parce que j'ai rompu le contrat: Foodisait qu'il fournissait HashMap s, mais maintenant il fournit TreeMaps à la place. Nous devons donc réparer SpecialFoo maintenant (et ce genre de chose peut onduler grâce à une base de code).

Sauf si j'avais une très bonne raison de partager que mon implémentation utilisait un HashMap (et cela se produit), ce que j'aurais dû faire était de déclarer getThings et getMoreThings comme retournant simplement Map<String, Object> sans être plus spécifique que cela. En fait, à moins d'une bonne raison de faire autre chose, même dans Foo, je devrais probablement déclarer things et moreThings comme Map, pas 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...
}

Notez comment j'utilise maintenant Map<String, Object> partout où je peux, seulement en étant spécifique lorsque je crée les objets réels.

Si j'avais fait cela, alors Marie aurait fait cela:
class SpecialFoo extends Foo {
    private void doSomething(Map<String, Object> t) { // <== Changed
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }
}

...et changer Foo n'aurait pas empêché SpecialFoo d'arrêter la compilation.

Interfaces (et classes de base) ne révélons que ce qui est nécessaire , en gardant notre flexibilité sous les couvertures pour apporter des modifications appropriées. En général, nous voulons que nos références soient aussi basiques que possible. Si nous n'avons pas besoin de savoir que c'est un HashMap, appelez-le simplement un Map.

Ce n'est pas une règle aveugle, mais en général, le codage vers l'interface la plus générale va être moins fragile que le codage vers quelque chose de plus spécifique. Si je m'en étais souvenu, je n'aurais pas créé un Foo qui a mis Mary en échec avec SpecialFoo. Si Mary s'était souvenue de cela, alors même si j'ai foiré Foo, elle aurait déclaré sa méthode privée avec Map au lieu de HashMap et mon changement de contrat de Foo ne l'aurait pas impactée code.

Parfois, vous ne pouvez pas le faire, parfois vous devez être précis. Mais à moins que vous n'ayez une raison d'être, errez vers l'interface la moins spécifique.

 362
Author: T.J. Crowder, 2017-04-20 08:25:12

Carte est une interface HashMap met en œuvre. La différence est que dans la deuxième implémentation, votre référence à HashMap ne permettra que l'utilisation des fonctions définies dans l'interface Map, tandis que la première permettra l'utilisation de toutes les fonctions publiques dans HashMap (qui inclut l'interface Map).

Cela aura probablement plus de sens si vous lisez Le tutoriel d'interface de Sun

 52
Author: Graphics Noob, 2015-05-25 10:56:08

J'allais juste le faire comme un commentaire sur la réponse acceptée mais c'est devenu trop funky (je déteste ne pas avoir de sauts de ligne)

Ah, donc la différence est que dans général, la carte a certaines méthodes associé avec elle. mais il y a différentes façons ou la création d'une carte, tels que en tant que HashMap, et ces différentes façons fournir des méthodes uniques qui ne sont pas toutes les cartes ont.

Exactement Exactly et vous voulez toujours utiliser l'interface la plus générale possible. Considérez ArrayList vs LinkedList. Énorme différence dans la façon dont vous les utilisez, mais si vous utilisez "Liste", vous pouvez basculer entre eux facilement.

En fait, vous pouvez remplacer le côté droit de l'initialiseur par une instruction plus dynamique. que diriez-vous de quelque chose comme ceci:

List collection;
if(keepSorted)
    collection=new LinkedList();
else
    collection=new ArrayList();

De cette façon, si vous allez remplir la collection avec un tri par insertion, vous utiliserez une liste chaînée (un tri par insertion dans une liste de tableaux est criminel.) Mais si vous n'avez pas besoin de le garder trié et que vous ajoutez simplement, vous utilisez une ArrayList (Plus efficace pour d'autres opérations).

C'est un assez gros tronçon ici parce que les collections ne sont pas le meilleur exemple, mais dans OO design, l'un des concepts les plus importants est d'utiliser la façade d'interface pour accéder à différents objets avec exactement le même code.

Modifier répondre au commentaire:

En ce qui concerne votre commentaire de carte ci-dessous, Oui, l'utilisation de l'interface "Map" vous limite uniquement à ces méthodes, sauf si vous renvoyez la collection de Map à HashMap (ce qui va COMPLÈTEMENT à l'encontre du but).

Souvent, vous allez créer un objet et le remplir en utilisant son type spécifique (HashMap), dans une sorte de méthode "create" ou "initialize", mais cette méthode retournera une "Map" qui n'a plus besoin d'être manipulée en tant que HashMap.

Si vous devez soit dit en passant, vous utilisez probablement la mauvaise interface ou votre code n'est pas assez structuré. Notez qu'il est acceptable qu'une section de votre code le traite comme un "HashMap "tandis que l'autre le traite comme une" Carte", mais cela devrait couler"vers le bas". pour que vous ne soyez jamais casting.

Notez également l'aspect semi-soigné des rôles indiqués par les interfaces. Une LinkedList fait une bonne pile ou file d'attente, une ArrayList fait une bonne pile mais une file d'attente horrible (encore une fois, une suppression provoquerait un décalage de la liste entière) donc LinkedList implémente l'interface de file d'attente, ArrayList ne le fait pas.

 17
Author: Bill K, 2009-08-28 19:05:34

entrez la description de l'image ici

Map ayant les implémentations suivantes,

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Arborescence De La Carte Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

Supposons que vous ayez créé une méthode (c'est juste du code spudo).

public void HashMap getMap(){
   return map;
}

Supposons que les exigences du projet changent à chaque fois comme suit,

  1. La méthode devrait renvoyer le contenu de la carte-Besoin de retourner HashMap.
  2. La méthode
  3. doit renvoyer la clé map dans l'ordre d'insertion - Besoin de changer le type de retour HashMap en LinkedHashMap.
  4. La méthode
  5. doit renvoyer les clés de carte dans l'ordre trié - Besoin de changer le type de retour LinkedHashMap en TreeMap.

Si votre méthode renvoie des classes spécifiques au lieu de l'interface Map, vous devez changer le type de retour de la méthode getMap() à chaque fois.

Mais, Si vous utilisez la fonctionnalité polymorphism de java, au lieu de renvoyer la classe spécifique utilisée interface Map, Il conduit la réutilisabilité du code et moins d'impact si tout changement d'exigence.

 16
Author: atish shimpi, 2015-02-16 10:19:26

Comme noté par TJ Crowder et Adamski, une référence est à une interface, l'autre à une implémentation spécifique de l'interface. Selon Joshua Block, vous devriez toujours essayer de coder sur des interfaces, pour vous permettre de mieux gérer les modifications apportées à l'implémentation sous - jacente-c'est-à-dire si HashMap n'était soudainement pas idéal pour votre solution et que vous deviez changer l'implémentation de la carte, vous pouvez toujours utiliser l'interface de la carte et changer le type

 12
Author: aperkins, 2009-08-28 16:53:00

Dans le deuxième exemple, la "carte" de référence est de type Map, qui est une interface implémentée par HashMap (et d'autres types de Map). Cette interface est un contrat indiquant que l'objet mappe les clés aux valeurs et prend en charge diverses opérations (par exemple put, get). Il ne dit rien sur l'implémentation du Map (dans ce cas, un HashMap).

La deuxième approche est généralement préférée car vous ne voudriez généralement pas exposer l'implémentation de map spécifique à méthodes utilisant le Map ou via une définition d'API.

 8
Author: Adamski, 2009-08-28 16:51:00

Map est letype statique de map, tandis que HashMap est letype dynamique de map. Cela signifie que le compilateur traitera votre objet map comme étant de type Map, même si au moment de l'exécution, il peut pointer vers n'importe quel sous-type de celui-ci.

Cette pratique de programmation contre les interfaces au lieu des implémentations a l'avantage supplémentaire de rester flexible: Vous pouvez par exemple remplacer le type dynamique de map à l'exécution, tant qu'il s'agit d'un sous-type de Map (par exemple LinkedHashMap), et modifiez le comportement de la carte à la volée.

Une bonne règle empirique est de rester aussi abstrait que possible au niveau de l'API: Si par exemple une méthode que vous programmez doit fonctionner sur des cartes, alors il suffit de déclarer un paramètre comme Map au lieu du type HashMap plus strict (car moins abstrait). De cette façon, le consommateur de votre API peut être flexible quant au type d'implémentation de carte qu'il souhaite transmettre à votre méthode.

 8
Author: Matthias, 2009-08-28 17:03:05

Vous créez les mêmes cartes.

Mais vous pouvez combler la différence lorsque vous l'utiliserez. Avec le premier cas, vous pourrez utiliser des méthodes HashMap spéciales (mais je ne me souviens de personne vraiment utile), et vous pourrez le passer en tant que paramètre HashMap:

public void foo (HashMap<String, Object) { ... }

...

HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;

foo (m1);
foo ((HashMap<String, Object>)m2); 
 3
Author: Roman, 2009-08-28 16:56:34

En ajoutant à la réponse votée en haut et à beaucoup d'autres ci-dessus en soulignant le "plus générique, mieux", je voudrais creuser un peu plus.

Map est le contrat de structure alors que HashMap est une implémentation fournissant ses propres méthodes pour traiter différents problèmes réels: comment calculer l'index, quelle est la capacité et comment l'incrémenter, comment insérer, comment garder l'index unique, etc.

Examinons le code source:

Dans Map, nous avons la méthode de containsKey(Object key):

boolean containsKey(Object key);

JavaDoc:

Java booléen.util.Cartographie.containsValue (Valeur de l'objet)

Renvoie true si cette carte mappe une ou plusieurs clés à la valeur spécifiée. Plus formellement, renvoie true si et seulement si cette carte contient au moins un mappage vers une valeur v telle que (value==null ? v==null : value.equals(v)). Cette opération nécessitera probablement du temps linéaire dans la taille de la carte pour la plupart des implémentations de l'interface de carte.

Paramètres:valeur

Valeur dont la présence dans cette carte est à betested

Renvoie: true

Si cette carte mappe une ou plusieurs clés au{[11 spécifié]}

ValueThrows:

ClassCastException-si la valeur est d'un type inapproprié pour cette carte (facultatif)

NullPointerException-si la valeur spécifiée est null et que cette carte n'autorise pas les valeurs null (facultatif)

Il nécessite ses implémentations pour l'implémenter, mais le" comment " est à sa liberté, seulement pour assurer il retourne corriger.

Dans HashMap:

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

Il s'avère que HashMap utilise hashcode pour tester si cette carte contient la clé. Donc, il a l'avantage de l'algorithme de hachage.

 2
Author: WesternGun, 2017-09-28 08:00:21

Map est l'interface et Hashmap est la classe qui implémente cela.

Donc, dans cette implémentation, vous créez les mêmes objets

 1
Author: Diego Dias, 2009-08-28 16:59:20

Map est une interface et Hashmap est une classe qui implémente Map Interface

 1
Author: , 2017-05-09 06:24:26

HashMap est une implémentation de Map donc c'est tout à fait la même chose mais a la méthode" clone () " comme je le vois dans le guide de référence))

 0
Author: kolyaseg, 2014-09-23 12:49:02
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();  

Tout d'Abord Map est une interface qu'il a mises en œuvre différentes comme - HashMap, TreeHashMap, LinkedHashMap etc. L'interface fonctionne comme une super classe pour la classe d'implémentation. Donc, selon la règle de OOP, toute classe concrète qui implémente Map est également un Map. Cela signifie que nous pouvons affecter/mettre n'importe quelle variable de type HashMap à une variable de type Map sans aucun type de casting.

Dans ce cas, nous pouvons assigner map1 à map2 sans aucune diffusion ni perte de données -

map2 = map1
 0
Author: Razib, 2015-02-09 19:10:08