Me sorprendió el hecho de que Map<?,?>no es un Collection<?>.
Pensé que tendría mucho sentido si se declaraba como tal:
public interface Map<K,V> extends Collection<Map.Entry<K,V>>
Después de todo, a Map<K,V>es una colección de Map.Entry<K,V>, ¿no?
Entonces, ¿hay una buena razón por la cual no se implementa como tal?
Gracias a Cletus por la respuesta más autorizada, pero todavía me pregunto por qué, si ya puede ver un Map<K,V>as Set<Map.Entries<K,V>>(vía entrySet()), no solo extiende esa interfaz.
Si a
Mapes aCollection, ¿cuáles son los elementos? La única respuesta razonable es "pares clave-valor"
¡Exactamente, interface Map<K,V> extends Set<Map.Entry<K,V>>sería genial!
pero esto proporciona una
Mapabstracción muy limitada (y no particularmente útil) .
Pero si ese es el caso, ¿ entrySetpor qué lo especifica la interfaz? Debe ser útil de alguna manera (¡y creo que es fácil defender esa posición!).
No puede preguntar a qué valor se asigna una clave determinada, ni puede eliminar la entrada de una clave determinada sin saber a qué valor se asigna.
¡No estoy diciendo que eso es todo Map! ¡Puede y debe mantener todos los demás métodos (excepto entrySet, que ahora es redundante)!
fuente

Respuestas:
De las Preguntas frecuentes sobre el diseño de la API de colecciones Java :
Actualización: creo que la cita responde a la mayoría de las preguntas. Vale la pena enfatizar la parte acerca de que una colección de entradas no es una abstracción particularmente útil. Por ejemplo:
permitiría:
(suponiendo un
entry()método que crea unaMap.Entryinstancia)Maps requieren claves únicas, por lo que esto violaría esto. O si impone claves únicas en unaSetde las entradas, no es realmente unaSeten el sentido general. Es unaSetcon más restricciones.Podría decirse que la relación
equals()/hashCode()paraMap.Entryfue puramente clave, pero incluso eso tiene problemas. Más importante aún, ¿realmente agrega algún valor? Es posible que esta abstracción se descomponga una vez que comience a mirar los casos de esquina.Vale la pena señalar que el
HashSetrealmente se implementa como unHashMap, no al revés. Esto es puramente un detalle de implementación, pero no obstante es interesante.La razón principal para
entrySet()existir es simplificar el recorrido para que no tenga que atravesar las teclas y luego hacer una búsqueda de la clave. No lo tome como evidencia prima facie de que aMapdebería ser unaSetde entradas (en mi humilde opinión).fuente
entrySet()sí es compatibleremove, mientras que no es compatibleadd(probablemente arrojaUnsupportedException). Así que veo su punto, pero de nuevo, también veo el punto del OP ... (Mi propia opinión es la que se indica en mi propia respuesta ...)addse pueden manejar de la misma manera que lo hace laentrySet()vista: permite algunas operaciones y no permite otras. Una respuesta natural, por supuesto, es "¿Qué tipo deSetno es compatibleadd?" - Bueno, si tal comportamiento es aceptableentrySet(), ¿por qué no lo esthis? Dicho esto, ya estoy convencido de por qué esta no es una idea tan buena como alguna vez pensé, pero sigo pensando que merece más debate, aunque solo sea para enriquecer mi propia comprensión de lo que hace un buen diseño API / OOP.CollectionextenderMap?Si bien ha recibido varias respuestas que cubren su pregunta de manera bastante directa, creo que podría ser útil retroceder un poco y analizar la pregunta de manera más general. Es decir, no mirar específicamente cómo se escribe la biblioteca Java y ver por qué se escribe de esa manera.
El problema aquí es que la herencia solo modela un tipo de comunidad. Si elige dos cosas que parecen "de colección", probablemente pueda elegir entre 8 o 10 cosas que tienen en común. Si elige un par diferente de cosas "de colección", también tendrán 8 o 10 cosas en común, pero no serán las mismas 8 o 10 cosas que el primer par.
Si nos fijamos en una docena de diferentes "colección como" cosas, prácticamente cada una de ellas tendrá probablemente algo así como 8 o 10 características en común con al menos otro - pero si nos fijamos en lo que se comparte a través de cada uno de ellos, te quedas prácticamente sin nada.
Esta es una situación en la que la herencia (especialmente la herencia única) simplemente no se modela bien. No hay una línea divisoria clara entre cuáles de ellas son realmente colecciones y cuáles no, pero si desea definir una clase de Colección significativa, no podrá dejar algunas. Si deja solo unos pocos, su clase de Colección solo podrá proporcionar una interfaz bastante escasa. Si deja más, podrá darle una interfaz más rica.
Algunos también toman la opción de decir básicamente: "este tipo de colección admite la operación X, pero no se le permite usarla, derivando de una clase base que define X, pero el intento de usar la clase derivada 'X falla (p. Ej. , lanzando una excepción).
Eso todavía deja un problema: casi independientemente de cuál deje fuera y cuál ingrese, tendrá que trazar una línea dura entre las clases que están dentro y las que están fuera. No importa dónde dibuje esa línea, se quedará con una división clara, más bien artificial, entre algunas cosas que son bastante similares.
fuente
Supongo que el por qué es subjetivo.
En C #, creo que
Dictionaryextiende o al menos implementa una colección:En Pharo Smalltak también:
Pero hay una asimetría con algunos métodos. Por ejemplo,
collect:will toma asociación (el equivalente de una entrada), mientrasdo:toma los valores. Proporcionan otro métodokeysAndValuesDo:para iterar el diccionario por entrada.Add:toma una asociación, peroremove:ha sido "suprimida":Por lo tanto, es definitivamente factible, pero conduce a otros problemas relacionados con la jerarquía de clases.
Lo que es mejor es subjetivo.
fuente
La respuesta de cletus es buena, pero quiero agregar un enfoque semántico. Para combinar ambos no tiene sentido, piense en el caso de que agregue un par clave-valor a través de la interfaz de recopilación y la clave ya existe. La interfaz de mapa solo permite un valor asociado con la clave. Pero si elimina automáticamente la entrada existente con la misma clave, la colección tiene después del agregado el mismo tamaño que antes, muy inesperado para una colección.
fuente
Setfunciona ySetse implementaCollection.Las colecciones de Java están rotas. Falta una interfaz, la de Relación. Por lo tanto, el mapa extiende la relación extiende el conjunto. Las relaciones (también llamadas mapas múltiples) tienen pares de nombre-valor únicos. Los mapas (también conocidos como "Funciones") tienen nombres únicos (o claves) que, por supuesto, se asignan a valores. Las secuencias extienden Mapas (donde cada clave es un entero> 0). Las bolsas (o conjuntos múltiples) extienden Mapas (donde cada clave es un elemento y cada valor es la cantidad de veces que el elemento aparece en la bolsa).
Esta estructura permitiría la intersección, unión, etc. de una gama de "colecciones". Por lo tanto, la jerarquía debería ser:
Sun / Oracle / Java ppl: hazlo bien la próxima vez. Gracias.
fuente
Si observa la estructura de datos respectiva, puede adivinar fácilmente por qué
Mapno forma parte de ellaCollection. Cada unoCollectionalmacena un valor único donde como unMappar clave-valor almacena. Por lo tanto, los métodos en laCollectioninterfaz son incompatibles para laMapinterfaz. Por ejemplo enCollectiontenemosadd(Object o). ¿Cuál sería tal implementación enMap. No tiene sentido tener un método asíMap. En cambio tenemos unput(key,value)método enMap.Mismo argumento se aplica a
addAll(),remove()yremoveAll()métodos. Entonces, la razón principal es la diferencia en la forma en que los datos se almacenan enMapyCollection. Además, si recuerda laCollectioninterfaz implementadaIterable, es decir, cualquier interfaz con.iterator()método debe devolver un iterador que debe permitirnos iterar sobre los valores almacenados en elCollection. Ahora, ¿qué devolvería tal método para aMap? ¿Un iterador clave o un iterador de valor? Esto tampoco tiene sentido.Hay formas en que podemos iterar sobre las claves y los almacenes de valores en ay
Mapasí es como es parte delCollectionmarco.fuente
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.¿Puedes mostrar un código de ejemplo para esto?add(Object o)sería agregar unEntry<ClassA, ClassB>objeto. AMappuede considerarse unCollection of TuplesEn realidad, si lo fuera
implements Map<K,V>, Set<Map.Entry<K,V>>, entonces tiendo a estar de acuerdo ... Parece incluso natural. Pero eso no funciona muy bien, ¿verdad? Digamos que tenemosHashMap implements Map<K,V>, Set<Map.Entry<K,V>,LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V>etc ... eso está bien, pero si lo hubiera hechoentrySet(), nadie olvidará implementar ese método, y puede estar seguro de que puede obtener entrySet para cualquier Mapa, mientras que no lo está si espera que el implementador ha implementado ambas interfaces ...La razón por la que no quiero tener
interface Map<K,V> extends Set<Map.Entry<K,V>>es simplemente, porque habrá más métodos. Y después de todo, son cosas diferentes, ¿verdad? También muy prácticamente, si llegomap.a IDE, no quiero ver.remove(Object obj), y.remove(Map.Entry<K,V> entry)porque no puedo hacerhit ctrl+space, r, returny terminar con eso.fuente
Map<K,V>no debe extenderseSet<Map.Entry<K,V>>ya que:Map.Entryelectrónicos con la misma clave a la mismaMap, peroMap.Entrys con la misma clave a la mismaSet<Map.Entry>.fuente
Recto y simple. Collection es una interfaz que solo espera un Object, mientras que Map requiere Two.
fuente