Pourquoi Java Map n'étend-il pas la collection?


, j'ai été surpris par le fait que Map<?,?> n'est pas Collection<?>.

Je pensais 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, un Map<K,V> est une collection de Map.Entry<K,V>, n'est-ce pas?

Y a-t-il donc une bonne raison pour laquelle il n'est pas implémenté en tant que tel?


Merci à Cletus pour une réponse la plus autoritaire, mais je me demande toujours pourquoi, si vous pouvez déjà voir un Map<K,V> comme Set<Map.Entries<K,V>> (via entrySet()), cela ne s'étend pas seulement interface à la place.

Si Map est un Collection, quels sont les éléments? La seule réponse raisonnable est "paires Clé-valeur"

Exactement, interface Map<K,V> extends Set<Map.Entry<K,V>> serait génial!

Mais cela fournit une abstraction très limitée (et pas particulièrement utile) Map.

Mais si c'est le cas, pourquoi entrySet est-il 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, vous ne pouvez pas non plus supprimer l'entrée d'une clé donnée sans savoir à quelle valeur elle correspond.

Je ne dis pas que c'est tout ce qu'il y a à Map! Il peut et devrait garder toutes les autres méthodes (sauf entrySet, qui est redondant maintenant)!

Author: toniedzwiedz, 2010-04-16

9 answers

De la Java Collections de Conception d'API FAQ:

Pourquoi la carte n'étend-elle pas la collection?

C'était par conception. Nous avons le sentiment que les mappages ne sont pas des collections et les collections ne sont pas des mappages. Ainsi, il cela a peu de sens pour que la carte s'étende l'interface de collecte (ou vice inversement).

Si une Carte est une Collection, quelles sont les éléments? La seule réponse raisonnable est "paires Clé-valeur", mais ce fournit un très limité (et non particulièrement utile) Abstraction de carte. Vous ne pouvez pas demander quelle valeur une clé donnée cartes à, vous ne pouvez pas supprimer l'entrée pour une clé donnée sans savoir quoi valeur des cartes de.

Collection pourrait être faite pour étendre Carte, mais cela soulève la question: quelles sont les clés? Il n'y a pas vraiment réponse satisfaisante, et en forçant un conduit à une interface non naturelle.

Les cartes peuvent être considérées comme des collections (de clés, valeurs ou paires), et ce fait est reflété dans le trois " Collection afficher les opérations " sur les cartes (jeu de clés, entrySet, et valeurs). Alors qu'il est, dans principe, possible de voir une liste comme une carte mappant les indices aux éléments, cela a la propriété désagréable que la suppression d'un élément de la Liste modifie la clé associée à chaque élément avant l'élément supprimé. C'est pourquoi nous n'avons pas de vue cartographique opération sur les Listes.

Mise à Jour: je pense que la citation répond à la plupart des questions. Cela vaut la peine de souligner le partie sur une collection d'entrées n'étant pas une abstraction particulièrement utile. Par exemple:

Set<Map.Entry<String,String>>

Permettrait:

set.add(entry("hello", "world"));
set.add(entry("hello", "world 2");

(en supposant une méthode entry() qui crée une instance Map.Entry)

Maps nécessitent des clés uniques, ce qui violerait cela. Ou si vous imposez des clés uniques à un Set d'entrées, ce n'est pas vraiment un Set au sens général. C'est un Set avec d'autres restrictions.

On pourrait dire que le equals()/hashCode() la relation pour {[3] } é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 pouvez trouver cette abstraction se décompose une fois que vous commencez à regarder les cas d'angle.

Il convient de noter que le HashSet est réellement implémenté en tant que HashMap, et non l'inverse. Ceci est purement un détail d'implémentation mais est néanmoins intéressant.

La principale raison pour laquelle entrySet() existe est de simplifier la traversée afin que vous n'ayez pas à parcourir les clés, puis à faire une recherche de la clé. Ne le prenez pas comme une preuve prima facie qu'un Map devrait être un Set d'entrées (à mon humble avis).

 130
Author: cletus, 2014-04-03 13:51:54

Bien que vous ayez obtenu un certain nombre de réponses qui couvrent votre question assez directement, je pense qu'il pourrait être utile de prendre un peu de recul et de regarder la question un peu plus généralement. Autrement dit, ne pas regarder spécifiquement comment la bibliothèque Java est écrite, et regarder pourquoi elle est écrite de cette façon.

Le problème ici est que l'héritage ne modélise que un type de commonalité. Si vous choisissez deux choses qui semblent toutes deux "comme une collection", vous pouvez probablement choisir un 8 ou 10 les choses qu'ils ont en commun. Si vous choisissez une paire différente de choses" de type collection", elles auront également 8 ou 10 choses en commun but mais elles ne seront pas les mêmes 8 ou 10 choses que la première paire.

Si vous regardez une douzaine de choses "de type collection" différentes, pratiquement chacune d'entre elles aura probablement quelque chose comme 8 ou 10 caractéristiques en commun avec au moins une autre characteristics mais si vous regardez ce qui est partagé entre chaque l'un d'entre eux, il vous reste pratiquement rien.

C'est une situation que l'héritage (en particulier l'héritage unique) ne modélise tout simplement pas bien. 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 de collection significative, vous devez en laisser certaines. Si vous n'en laissez que quelques-uns, votre classe de collection ne pourra fournir qu'une interface assez clairsemée. Si vous en laissez plus, vous serez en mesure de lui donner un plus riche interface.

Certains prennent également l'option de dire fondamentalement: "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 tenter 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 mettez, vous allez devoir tracer une ligne dure entre les classes qui sont dedans et ce qui est dehors. Peu importe où vous dessinez cette ligne, vous allez vous retrouver avec une division claire, plutôt artificielle, entre certaines choses qui sont assez similaires.

 11
Author: Jerry Coffin, 2010-04-16 19:51:06

Je suppose que le pourquoi, est subjective.

En C#, je pense que Dictionary étend ou au moins implémente une collection:

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
    ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, 
    IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback

Dans Pharo Smalltak aussi bien:

Collection subclass: #Set
Set subclass: #Dictionary

Mais il y a une asymétrie avec certaines méthodes. Par exemple, collect: prendra l'association (l'équivalent d'une entrée), tandis que do: prendra les valeurs. Ils fournissent une autre méthode keysAndValuesDo: pour itérer le dictionnaire par entrée. Add: prend une association, mais remove: a été "supprimé":

remove: anObject
self shouldNotImplement 

Donc c'est définitivement faisable, mais conduit à d'autres problèmes concernant la hiérarchie des classes.

Ce qui est mieux est subjectif.

 10
Author: ewernli, 2012-12-05 22:50:24

La réponse de cletus est bonne, mais je veux ajouter une approche sémantique. Pour combiner les deux n'a aucun sens, pensez au cas où vous ajoutez une paire clé-valeur via l'interface de collection et 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 avec la même clé, la collection a après l'ajout de la même taille que très inattendu pour une collection.

 3
Author: Mnementh, 2010-04-16 10:02:46

Les collections Java sont cassées. Il manque une interface, celle de la Relation. Par conséquent, la relation Map extends étend l'ensemble. Les relations (également appelées multi-cartes) ont des paires nom-valeur uniques. Les cartes (aka "Fonctions"), ont des noms uniques (ou des clés) 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 où l'élément apparaît dans le sac).

Cette structure serait autoriser l'intersection, l'union, etc. d'une gamme de "collections". Par conséquent, la hiérarchie devrait être:

                                Set

                                 |

                              Relation

                                 |

                                Map

                                / \

                             Bag Sequence

Sun/Oracle/Java ppl - veuillez l'obtenir correctement la prochaine fois. Grâce.

 3
Author: xagyg, 2010-07-07 13:35:58

Map<K,V> ne devrait pas s'étendre Set<Map.Entry<K,V>> depuis:

  • Vous ne pouvez pas ajouter des Map.Entrydifférents avec la même clé au même Map, mais
  • Vous peut ajouter différents Map.Entrys avec la même clé pour le même Set<Map.Entry>.
 1
Author: einpoklum, 2015-02-21 18:52:18

Si vous regardez à la structure de données, vous pouvez facilement deviner pourquoi Map n'est pas une partie de Collection. Chaque Collection stocke une seule valeur où en tant que Map stocke la paire clé-valeur. Ainsi, les méthodes dans l'interface Collection sont incompatibles pour l'interface Map. Par exemple, dans Collection nous add(Object o). Quelle serait cette mise en œuvre dans Map. Cela n'a pas de sens d'avoir une telle méthode dans Map. Au lieu de cela, nous avons une méthode put(key,value) dans Map.

Même argument vaut pour les addAll(), remove(), et removeAll() méthodes. Donc, la raison principale est la différence dans la façon dont les données sont stockées dans Map et Collection. De plus, si vous vous souvenez de l'interface Collection implémentée dans l'interface Iterable, c'est-à-dire que toute interface avec la méthode .iterator() doit renvoyer un itérateur qui doit nous permettre d'itérer sur les valeurs stockées dans le Collection. Maintenant, que retournerait une telle méthode pour un Map? Itérateur de clé ou itérateur de valeur? Cela n'a pas de sens.

Il y a des façons dont nous pouvons itérer sur les clés et les magasins de valeurs dans un Map et c'est comment il fait partie du cadre Collection.

 1
Author: Mayur Ingle, 2016-10-04 21:06:36

Exactement, interface Map<K,V> extends Set<Map.Entry<K,V>> serait génial!

En Fait, si c'était 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 avons HashMap implements Map<K,V>, Set<Map.Entry<K,V>, LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V> etc... c'est tout bon, mais si vous aviez entrySet(), personne n'oubliera d'implémenter cette méthode, et vous pouvez être sûr que vous pouvez obtenir entrySet pour n'importe quelle Carte, alors que vous ne l'êtes pas si vous espérez que l'implémenteur a implémenté les deux interfaces...

La raison pour laquelle je ne veux pas have interface Map<K,V> extends Set<Map.Entry<K,V>> est simplement, car il y aura plus de méthodes. Et après tout, ce sont des choses différentes, non? Aussi très pratiquement, si je frappe map. dans ID, je ne veux pas voir .remove(Object obj), et .remove(Map.Entry<K,V> entry) parce que je ne peux pas faire hit ctrl+space, r, return et en avoir fini avec.

 0
Author: Enno Shioji, 2010-04-16 10:23:44

Droit et simple. Collection est une interface qui n'attend qu'un seul objet, alors que Map en nécessite Deux.

Collection(Object o);
Map<Object,Object>
 -2
Author: Trinadh Koya, 2018-12-18 04:25:54