J'ai été surpris par le fait que ce Map<?,?>
n'est pas un fichier Collection<?>
.
J'ai pensé que cela aurait beaucoup de sens s'il était déclaré comme tel:
public interface Map<K,V> extends Collection<Map.Entry<K,V>>
Après tout, Map<K,V>
c'est une collection de Map.Entry<K,V>
, n'est-ce pas?
Alors, y a-t-il une bonne raison pour laquelle ce n'est pas mis en œuvre en tant que tel?
Merci à Cletus pour une réponse des plus fiables, mais je me demande toujours pourquoi, si vous pouvez déjà afficher un Map<K,V>
as Set<Map.Entries<K,V>>
(via entrySet()
), cela ne fait pas qu'étendre cette interface à la place.
Si a
Map
est aCollection
, quels sont les éléments? La seule réponse raisonnable est "Paires clé-valeur"
Exactement, ce interface Map<K,V> extends Set<Map.Entry<K,V>>
serait génial!
mais cela fournit une
Map
abstraction très limitée (et pas particulièrement utile) .
Mais si tel est le cas, pourquoi est entrySet
spécifié par l'interface? Cela doit être utile en quelque sorte (et je pense qu'il est facile de plaider pour cette position!).
Vous ne pouvez pas demander à quelle valeur une clé donnée correspond, ni supprimer l'entrée pour une clé donnée sans savoir à quelle valeur elle correspond.
Je ne dis pas que c'est tout ce qu'il y a à faire Map
! Il peut et doit conserver toutes les autres méthodes (sauf entrySet
, qui est redondante maintenant)!
la source
Réponses:
À partir de la FAQ sur la conception d'API de collections Java :
Mise à jour: Je pense que la citation répond à la plupart des questions. Cela vaut la peine de souligner la partie sur une collection d'entrées n'étant pas une abstraction particulièrement utile. Par exemple:
permettrait:
(en supposant une
entry()
méthode qui crée uneMap.Entry
instance)Map
s nécessitent des clés uniques, ce qui enfreindrait cela. Ou si vous imposez des clés uniques à uneSet
des entrées, ce n'est pas vraiment uneSet
au sens général. C'est unSet
avec d'autres restrictions.On peut dire que la relation
equals()
/hashCode()
pourMap.Entry
était purement sur la clé, mais même cela a des problèmes. Plus important encore, cela ajoute-t-il vraiment de la valeur? Vous constaterez peut-être que cette abstraction s'effondre une fois que vous commencez à regarder les cas d'angle.Il convient de noter que le
HashSet
est en fait mis en œuvre comme unHashMap
, et non l'inverse. Ceci est purement un détail de mise en œuvre mais est néanmoins intéressant.La principale raison
entrySet()
d'exister est de simplifier la traversée afin de ne pas avoir à parcourir les clés, puis à rechercher la clé. Ne le prenez pas comme une preuve prima facie que aMap
devrait être uneSet
des entrées (à mon humble avis).la source
entrySet()
prend en chargeremove
, alors qu'elle ne prend pas en chargeadd
(jette probablementUnsupportedException
). Donc je vois votre point, mais là encore, je vois aussi le point du PO. (Ma propre opinion est comme indiqué dans ma propre réponse ..)add
peuvent être gérées comme le fait laentrySet()
vue: autorisez certaines opérations et n'en autorisez pas d'autres. Une réponse naturelle, bien sûr, est "Quel genre deSet
ne prend pas en chargeadd
?" - eh bien, si un tel comportement est acceptable pourentrySet()
, alors pourquoi n'est-il pas acceptable pourthis
? Cela dit, je suis déjà principalement convaincu de la raison pour laquelle ce n'est pas une si bonne idée que je le pensais autrefois, mais je pense toujours que cela mérite un débat plus approfondi, ne serait-ce que pour enrichir ma propre compréhension de ce qui fait une bonne conception d'API / POO.Collection
-Map
t-il s'étendre ?Bien que vous ayez obtenu un certain nombre de réponses qui couvrent assez directement votre question, je pense qu'il pourrait être utile de prendre un peu de recul et d'examiner la question d'un peu plus généralement. Autrement dit, ne pas regarder spécifiquement comment la bibliothèque Java est écrite, et pourquoi elle est écrite de cette façon.
Le problème ici est que l'héritage ne modélise qu'un seul type de points communs. Si vous choisissez deux choses qui ressemblent toutes les deux à une "collection", vous pouvez probablement choisir 8 ou 10 choses qu'elles ont en commun. Si vous choisissez une autre paire d'objets «de type collection», ils auront également 8 ou 10 objets en commun - mais ce ne sera pas les mêmes 8 ou 10 objets que la première paire.
Si vous regardez une douzaine d'éléments "de type collection" différents, pratiquement chacun d'entre eux aura probablement 8 ou 10 caractéristiques en commun avec au moins un autre - mais si vous regardez ce qui est partagé entre chacun d' entre eux d'entre eux, il ne vous reste pratiquement rien.
Il s'agit d'une situation que l'héritage (en particulier l'héritage unique) ne modélise pas correctement. Il n'y a pas de ligne de démarcation nette entre celles qui sont vraiment des collections et celles qui ne le sont pas - mais si vous voulez définir une classe Collection significative, vous êtes obligé de laisser certaines d'entre elles de côté. Si vous n'en laissez que quelques-uns, votre classe Collection ne pourra fournir qu'une interface assez clairsemée. Si vous en laissez plus, vous pourrez lui donner une interface plus riche.
Certains prennent aussi l'option de dire en gros: "ce type de collection prend en charge l'opération X, mais vous n'êtes pas autorisé à l'utiliser, en dérivant d'une classe de base qui définit X, mais en essayant d'utiliser la classe dérivée 'X échoue (par exemple , en lançant une exception).
Cela laisse toujours un problème: presque indépendamment de ce que vous laissez de côté et de ce que vous avez mis, vous allez devoir tracer une ligne de démarcation entre ce que les classes sont et ce qui ne l'est pas. Peu importe où vous tracez cette ligne, il vous restera une division claire, plutôt artificielle, entre certaines choses qui sont assez similaires.
la source
Je suppose que le pourquoi est subjectif.
En C #, je pense
Dictionary
étend ou au moins implémente une collection:Dans Pharo Smalltak également:
Mais il existe une asymétrie avec certaines méthodes. Par exemple,
collect:
will prend l'association (l'équivalent d'une entrée), tandis quedo:
prend les valeurs. Ils fournissent une autre méthodekeysAndValuesDo:
pour itérer le dictionnaire par entrée.Add:
prend une association, maisremove:
a été "supprimée":C'est donc définitivement faisable, mais conduit à d'autres problèmes concernant la hiérarchie des classes.
Ce qui est mieux est subjectif.
la source
La réponse de cletus est bonne, mais je veux ajouter une approche sémantique. Combiner les deux n'a aucun sens, pensez au cas où vous ajoutez une paire clé-valeur via l'interface de collecte et que la clé existe déjà. L'interface Map n'autorise qu'une seule valeur associée à la clé. Mais si vous supprimez automatiquement l'entrée existante avec la même clé, la collection a après l'ajout de la même taille qu'avant - très inattendu pour une collection.
la source
Set
fonctionne etSet
ne mettre en œuvreCollection
.Les collections Java sont cassées. Il manque une interface, celle de Relation. Par conséquent, la carte étend la relation étend l'ensemble. Les relations (également appelées multi-maps) ont des paires nom-valeur uniques. Les cartes (également appelées «fonctions») ont des noms (ou clés) uniques qui, bien sûr, correspondent à des valeurs. Les séquences étendent les cartes (où chaque clé est un entier> 0). Les sacs (ou multi-ensembles) étendent les cartes (où chaque clé est un élément et chaque valeur est le nombre de fois que l'élément apparaît dans le sac).
Cette structure permettrait l'intersection, l'union, etc. d'une gamme de "collections". Par conséquent, la hiérarchie devrait être:
Sun / Oracle / Java ppl - veuillez le faire la prochaine fois. Merci.
la source
Si vous regardez la structure de données respective, vous pouvez facilement deviner pourquoi
Map
ne fait pas partie deCollection
. ChacunCollection
stocke une valeur unique où en tant queMap
stocke une paire clé-valeur. Les méthodes dans l'Collection
interface sont donc incompatibles pour l'Map
interface. Par exemple dansCollection
nous avonsadd(Object o)
. Quelle serait une telle mise en œuvre dansMap
. Cela n'a pas de sens d'avoir une telle méthodeMap
. Au lieu de cela, nous avons uneput(key,value)
méthode dansMap
.Même argument va pour
addAll()
,remove()
et lesremoveAll()
méthodes. La raison principale est donc la différence dans la manière dont les données sont stockées dansMap
etCollection
. Aussi, si vous rappelez l'Collection
interface implémentée par l'Iterable
interface, c'est-à-dire que toute interface avec une.iterator()
méthode doit retourner un itérateur qui doit nous permettre d'itérer sur les valeurs stockées dans leCollection
. Maintenant, que reviendrait une telle méthode pour unMap
? Itérateur clé ou itérateur de valeur? Cela n'a pas non plus de sens.Il existe des façons dont nous pouvons parcourir les clés et les valeurs stockées dans un
Map
et c'est ainsi qu'il fait partie duCollection
framework.la source
There are ways in which we can iterate over keys and values stores in a Map and that is how it is a part of Collection framework.
Pouvez-vous montrer un exemple de code pour cela?add(Object o)
serait d'ajouter unEntry<ClassA, ClassB>
objet. AMap
peut être considéré comme unCollection of Tuples
En fait, si c'était le cas
implements Map<K,V>, Set<Map.Entry<K,V>>
, alors j'ai tendance à être d'accord ... Cela semble même naturel. Mais cela ne fonctionne pas très bien, non? Disons que nous avonsHashMap implements Map<K,V>, Set<Map.Entry<K,V>
,LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V>
etc ... qui est tout bon, mais si vous aviezentrySet()
, personne n'oubliez pas de mettre en œuvre cette méthode, et vous pouvez être sûr que vous pouvez obtenir entrySet pour une carte, alors que vous n'êtes pas si vous espérez que l'implémenteur a implémenté les deux interfaces ...La raison pour laquelle je ne veux pas en avoir
interface Map<K,V> extends Set<Map.Entry<K,V>>
est simplement parce qu'il y aura plus de méthodes. Et après tout, ce sont des choses différentes, non? Aussi très pratiquement, si je frappemap.
dans IDE, je ne veux pas voir.remove(Object obj)
, et.remove(Map.Entry<K,V> entry)
parce que je ne peux pas fairehit ctrl+space, r, return
et en finir avec.la source
Map<K,V>
ne devrait pas s'étendreSet<Map.Entry<K,V>>
puisque:Map.Entry
s avec la même clé à la mêmeMap
, maisMap.Entry
s avec la même clé à la mêmeSet<Map.Entry>
.la source
Droit et simple. Collection est une interface qui n'attend qu'un seul objet, alors que Map en nécessite deux.
la source