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 de manière interchangeable et je me demande si / comment elles sont différentes):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
java
dictionary
hashmap
Tony Stark
la source
la source
Réponses:
Il n'y a aucune différence entre les objets; vous en avez un
HashMap<String, Object>
dans les deux cas. Il y a une différence dans l' interface que vous avez avec l'objet. Dans le premier cas, l'interface estHashMap<String, Object>
, tandis que dans le second, c'estMap<String, Object>
. Mais l'objet sous-jacent est le même.L'avantage de l'utilisation
Map<String, Object>
est que vous pouvez changer l'objet sous-jacent en un autre type de carte sans rompre votre contrat avec le code qui l'utilise. Si vous le déclarez commeHashMap<String, Object>
, vous devez modifier votre contrat si vous souhaitez modifier l'implémentation sous-jacente.Exemple: disons que j'écris cette classe:
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 du code le sous-classant. Elle a quelque chose à faire avec les deux
things
etmoreThings
, donc naturellement, elle met cela dans une méthode commune, et elle utilise le même type que j'ai utilisé surgetThings
/getMoreThings
lors de la définition de sa méthode:Plus tard, je décide qu'en fait, c'est mieux si j'utilise à la
TreeMap
place deHashMap
inFoo
. Je mets à jourFoo
, en changeantHashMap
pourTreeMap
. Maintenant,SpecialFoo
ne compile plus, parce que j'ai rompu le contrat: jeFoo
disais qu'il fournissait desHashMap
s, mais maintenant il fournit à laTreeMaps
place. Nous devons donc corrigerSpecialFoo
maintenant (et ce genre de chose peut se répercuter sur une base de code).À moins d'avoir 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éclarergetThings
etgetMoreThings
de simplement revenirMap<String, Object>
sans être plus précis que cela. En fait, sauf une bonne raison de faire autre chose, même à l'intérieur,Foo
je devrais probablement déclarerthings
etmoreThings
commeMap
, nonHashMap
/TreeMap
:Notez comment j'utilise maintenant
Map<String, Object>
partout où je peux, en n'étant spécifique que lorsque je crée les objets réels.Si j'avais fait ça, alors Mary aurait fait ça:
... et changer
Foo
n'aurait pas faitSpecialFoo
arrêter la compilation.Les interfaces (et les classes de base) nous permettent de révéler tout ce qui est nécessaire , en gardant notre flexibilité sous les couvertures pour apporter les modifications appropriées. En général, nous voulons que nos références soient aussi simples que possible. Si nous n'avons pas besoin de savoir que c'est un
HashMap
, appelez-le simplement aMap
.Ce n'est pas une règle aveugle, mais en général, le codage vers l'interface la plus générale sera 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 aurait mis Mary en échecSpecialFoo
. Si Mary s'était souvenue de cela, alors même si j'avais gâchéFoo
, elle aurait déclaré sa méthode privée avecMap
au lieu deHashMap
et monFoo
contrat de changement n'aurait pas eu d'impact sur son code.Parfois, vous ne pouvez pas faire cela, parfois vous devez être précis. Mais à moins que vous n'ayez une raison de l'être, optez pour l'interface la moins spécifique.
la source
Map est une interface implémentée par HashMap . La différence est que dans la deuxième implémentation, votre référence à HashMap autorisera uniquement l'utilisation des fonctions définies dans l'interface Map, tandis que la première autorisera 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 sur l'interface de Sun
la source
La carte a les implémentations suivantes:
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Carte d'arbre
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Supposons que vous ayez créé une méthode (ce n'est qu'un pseudocode).
Supposons que les exigences de votre projet changent:
HashMap
.HashMap
enLinkedHashMap
.LinkedHashMap
parTreeMap
.Si votre méthode renvoie des classes spécifiques au lieu de quelque chose qui implémente l'
Map
interface, vous devez changer le type degetMap()
méthode de retour à chaque fois.Mais si vous utilisez la fonction de polymorphisme de Java, et au lieu de renvoyer des classes spécifiques, utilisez l'interface
Map
, cela améliore la réutilisabilité du code et réduit l'impact des changements d'exigences.la source
J'allais juste faire ça 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)
Exactement - et vous souhaitez toujours utiliser l'interface la plus générale possible. Considérez ArrayList vs LinkedList. Grande 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 quelque chose comme ça:
De cette façon, si vous allez remplir la collection avec un tri par insertion, vous utiliseriez une liste lié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 tronçon assez important ici parce que les collections ne sont pas le meilleur exemple, mais dans la conception OO, l'un des concepts les plus importants utilise la façade de l'interface pour accéder à différents objets avec le même code exact.
Modifier la réponse au commentaire:
En ce qui concerne votre commentaire de carte ci-dessous, Oui en utilisant l'interface "Carte" vous limite à ces méthodes uniquement, sauf si vous convertissez la collection de Map en 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 pas besoin d'être manipulé comme un HashMap plus.
Si jamais vous devez lancer un casting, vous utilisez probablement la mauvaise interface ou votre code n'est pas assez bien 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 "descendre". de sorte que vous ne lancez jamais.
Notez également l'aspect semi-soigné des rôles indiqué par les interfaces. Un LinkedList fait une bonne pile ou file d'attente, un ArrayList fait une bonne pile mais une file d'attente horrible (encore une fois, une suppression entraînerait un décalage de la liste entière) donc LinkedList implémente l'interface Queue, ArrayList non.
la source
Comme l'ont noté 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 en 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 pourriez toujours utiliser la carte interface et modifiez le type d'instanciation.
la source
Dans votre deuxième exemple, la référence "map" est de type
Map
, qui est une interface implémentée parHashMap
(et d'autres types deMap
). Cette interface est un contrat disant que l'objet mappe les clés aux valeurs et prend en charge diverses opérations (par exempleput
,get
). Il ne dit rien sur la mise en œuvre de laMap
(dans ce cas aHashMap
).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 carte spécifique à des méthodes utilisant la
Map
ou via une définition d'API.la source
La carte est le type de carte statique , tandis que HashMap est le type de carte dynamique . Cela signifie que le compilateur traitera votre objet de carte comme étant de type Map, même si à l'exécution, il peut pointer vers n'importe quel sous-type de celui-ci.
Cette pratique de programmation contre des interfaces au lieu d'implémentations présente l'avantage supplémentaire de rester flexible: vous pouvez par exemple remplacer le type dynamique de carte au moment de l'exécution, tant qu'il s'agit d'un sous-type de carte (par exemple LinkedHashMap), et modifier le comportement de la carte sur la mouche.
Une bonne règle de base 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 carte 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.
la source
En ajoutant à la réponse la plus votée et à plusieurs réponses ci-dessus soulignant le "plus générique, mieux", je voudrais creuser un peu plus.
Map
est le contrat de structure tandisHashMap
qu'une implémentation fournit ses propres méthodes pour traiter différents problèmes réels: comment calculer l'indice, quelle est la capacité et comment l'incrémenter, comment l'insérer, comment garder l'index unique, etc.Regardons le code source:
En
Map
nous avons la méthode decontainsKey(Object key)
:JavaDoc:
Il nécessite ses implémentations pour l'implémenter, mais le "comment faire" est à sa liberté, seulement pour s'assurer qu'il retourne correctement.
Dans
HashMap
:Il s'avère que
HashMap
utilise hashcode pour tester si cette carte contient la clé. Il a donc l'avantage de l'algorithme de hachage.la source
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:
la source
La carte est l'interface et Hashmap est une classe qui implémente l'interface de la carte
la source
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
la source
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))
la source
Tout d'abord
Map
est une interface différente qu'elle a mise en œuvre comme -HashMap
,TreeHashMap
,LinkedHashMap
etc. Interface fonctionne comme une classe super pour la classe d' implémentation. Ainsi, selon la règle de la POO, toute classe concrète qui implémenteMap
estMap
également un . Cela signifie que nous pouvons affecter / mettre n'importe quelleHashMap
variable de type à uneMap
variable de type sans aucun type de transtypage.Dans ce cas, nous pouvons attribuer
map1
àmap2
sans casting ni perte de données -la source