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
Map
es 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
Map
abstracción muy limitada (y no particularmente útil) .
Pero si ese es el caso, ¿ entrySet
por 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.Entry
instancia)Map
s requieren claves únicas, por lo que esto violaría esto. O si impone claves únicas en unaSet
de las entradas, no es realmente unaSet
en el sentido general. Es unaSet
con más restricciones.Podría decirse que la relación
equals()
/hashCode()
paraMap.Entry
fue 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
HashSet
realmente 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 aMap
debería ser unaSet
de 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 ...)add
se 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 deSet
no 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.Collection
extenderMap
?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
Dictionary
extiende 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
Set
funciona ySet
se 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é
Map
no forma parte de ellaCollection
. Cada unoCollection
almacena un valor único donde como unMap
par clave-valor almacena. Por lo tanto, los métodos en laCollection
interfaz son incompatibles para laMap
interfaz. Por ejemplo enCollection
tenemosadd(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 enMap
yCollection
. Además, si recuerda laCollection
interfaz 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
Map
así es como es parte delCollection
marco.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. AMap
puede considerarse unCollection of Tuples
En 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, return
y terminar con eso.fuente
Map<K,V>
no debe extenderseSet<Map.Entry<K,V>>
ya que:Map.Entry
electrónicos con la misma clave a la mismaMap
, peroMap.Entry
s 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