¿Cuál es la diferencia entre los siguientes mapas que creo (en otra pregunta, la gente respondió usándolos aparentemente de manera intercambiable y me pregunto si / cómo son diferentes):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
java
dictionary
hashmap
Tony Stark
fuente
fuente
Respuestas:
No hay diferencia entre los objetos; Tienes un
HashMap<String, Object>
en ambos casos. Hay una diferencia en la interfaz que tiene con el objeto. En el primer caso, la interfaz esHashMap<String, Object>
, mientras que en el segundo esMap<String, Object>
. Pero el objeto subyacente es el mismo.La ventaja de usar
Map<String, Object>
es que puede cambiar el objeto subyacente para que sea un tipo diferente de mapa sin romper su contrato con ningún código que lo esté usando. Si lo declara comoHashMap<String, Object>
, debe cambiar su contrato si desea cambiar la implementación subyacente.Ejemplo: Digamos que escribo esta clase:
La clase tiene un par de mapas internos de cadena-> objeto que comparte (a través de métodos de acceso) con subclases. Digamos que lo escribo con
HashMap
s para empezar porque creo que esa es la estructura apropiada para usar al escribir la clase.Más tarde, Mary escribe código subclasificándolo. Ella tiene algo que necesita hacer con ambos
things
ymoreThings
, naturalmente, lo pone en un método común, y usa el mismo tipo que usé engetThings
/getMoreThings
al definir su método:Más tarde, decido que en realidad, es mejor si lo uso en
TreeMap
lugar deHashMap
enFoo
. ActualizoFoo
, cambiandoHashMap
aTreeMap
. Ahora,SpecialFoo
ya no se compila, porque he roto el contrato:Foo
solía decir que proporcionabaHashMap
s, pero ahora está proporcionando en suTreeMaps
lugar. Así que tenemos que arreglarloSpecialFoo
ahora (y este tipo de cosas pueden pasar por una base de código).A menos que tuviera una buena razón para compartir que mi implementación estaba usando un
HashMap
(y eso sucede), lo que debería haber hecho fue declarargetThings
ygetMoreThings
simplemente regresarMap<String, Object>
sin ser más específico que eso. De hecho, salvo una buena razón para hacer otra cosa, incluso dentro deFoo
lo que probablemente debería declararthings
ymoreThings
comoMap
, noHashMap
/TreeMap
:Tenga en cuenta que ahora estoy usando
Map<String, Object>
todo lo que puedo, solo siendo específico cuando creo los objetos reales.Si hubiera hecho eso, entonces Mary habría hecho esto:
... y cambiar
Foo
no habría hecho queSpecialFoo
dejara de compilar.Las interfaces (y las clases base) nos permiten revelar todo lo que sea necesario , manteniendo nuestra flexibilidad bajo las cubiertas para realizar los cambios según corresponda. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un
HashMap
, simplemente llámelo aMap
.Esta no es una regla ciega, pero en general, la codificación de la interfaz más general será menos frágil que la codificación de algo más específico. Si lo hubiera recordado, no habría creado una
Foo
que hiciera fracasar a MarySpecialFoo
. Si Mary hubiera recordado eso, entonces, a pesar de que me equivoquéFoo
, habría declarado su método privado enMap
lugar deHashMap
y el cambio de miFoo
contrato no habría afectado su código.A veces no puedes hacer eso, a veces tienes que ser específico. Pero a menos que tenga una razón para estarlo, errar hacia la interfaz menos específica.
fuente
Map es una interfaz que implementa HashMap . La diferencia es que en la segunda implementación, su referencia a HashMap solo permitirá el uso de funciones definidas en la interfaz de Mapa, mientras que la primera permitirá el uso de cualquier función pública en HashMap (que incluye la interfaz de Mapa).
Probablemente tenga más sentido si lees el tutorial de interfaz de Sun
fuente
Map tiene las siguientes implementaciones:
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Mapa de arboles
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Supongamos que ha creado un método (esto es solo pseudocódigo).
Suponga que los requisitos de su proyecto cambian:
HashMap
.HashMap
aLinkedHashMap
.LinkedHashMap
aTreeMap
.Si su método devuelve clases específicas en lugar de algo que implementa la
Map
interfaz, debe cambiar el tipo degetMap()
método de retorno cada vez.Pero si usa la función de polimorfismo de Java y, en lugar de devolver clases específicas, usa la interfaz
Map
, mejora la reutilización del código y reduce el impacto de los cambios de requisitos.fuente
Solo iba a hacer esto como un comentario sobre la respuesta aceptada, pero se volvió demasiado funky (odio no tener saltos de línea)
Exactamente, y siempre desea utilizar la interfaz más general que pueda. Considere ArrayList vs LinkedList. Gran diferencia en cómo los usa, pero si usa "Lista" puede cambiar fácilmente entre ellos.
De hecho, puede reemplazar el lado derecho del inicializador con una declaración más dinámica. Qué tal algo como esto:
De esta manera, si va a completar la colección con un orden de inserción, usaría una lista vinculada (un orden de inserción en una lista de matriz es criminal). Pero si no necesita mantenerlo ordenado y solo está agregando, usa una ArrayList (más eficiente para otras operaciones).
Este es un tramo bastante grande aquí porque las colecciones no son el mejor ejemplo, pero en el diseño OO, uno de los conceptos más importantes es usar la fachada de la interfaz para acceder a diferentes objetos con exactamente el mismo código.
Editar respondiendo al comentario:
En cuanto a su comentario de mapa a continuación, Sí, usar la interfaz "Mapa" lo restringe solo a esos métodos, a menos que envíe la colección de Mapa a HashMap (que COMPLETAMENTE vence el propósito).
A menudo, lo que hará es crear un objeto y completarlo usando su tipo específico (HashMap), en algún tipo de método "crear" o "inicializar", pero ese método devolverá un "Mapa" que no necesita ser manipulado como un HashMap más.
Si alguna vez tiene que emitir por cierto, probablemente esté utilizando la interfaz incorrecta o su código no esté estructurado lo suficientemente bien. Tenga en cuenta que es aceptable que una sección de su código lo trate como un "HashMap" mientras que la otra lo trata como un "Mapa", pero esto debería fluir "hacia abajo". para que nunca lances
Observe también el aspecto semi-limpio de los roles indicados por las interfaces. Una LinkedList hace una buena pila o cola, una ArrayList hace una buena pila pero una cola horrible (una vez más, una eliminación provocaría un cambio en toda la lista), por lo que LinkedList implementa la interfaz de la cola, ArrayList no.
fuente
Como señalaron TJ Crowder y Adamski, una referencia es a una interfaz, la otra a una implementación específica de la interfaz. Según Joshua Block, siempre debe intentar codificar las interfaces, para permitirle manejar mejor los cambios en la implementación subyacente, es decir, si HashMap de repente no era ideal para su solución y necesita cambiar la implementación del mapa, aún podría usar el Mapa interfaz y cambiar el tipo de instanciación.
fuente
En su segundo ejemplo, la referencia de "mapa" es de tipo
Map
, que es una interfaz implementada porHashMap
(y otros tipos deMap
). Esta interfaz es un contrato que dice que el objeto asigna claves a valores y admite varias operaciones (por ejemploput
,get
). No dice nada acerca de la implementación deMap
(en este caso aHashMap
).Por lo general, se prefiere el segundo enfoque, ya que normalmente no querría exponer la implementación del mapa específico a los métodos que utilizan
Map
o mediante una definición de API.fuente
Mapa es el tipo de mapa estático , mientras que HashMap es el tipo dinámico de mapa. Esto significa que el compilador tratará su objeto de mapa como uno del tipo Mapa, aunque en tiempo de ejecución, puede apuntar a cualquier subtipo de él.
Esta práctica de programar contra interfaces en lugar de implementaciones tiene el beneficio adicional de permanecer flexible: por ejemplo, puede reemplazar el tipo dinámico de mapa en tiempo de ejecución, siempre que sea un subtipo de Mapa (por ejemplo, LinkedHashMap) y cambiar el comportamiento del mapa en la mosca.
Una buena regla general es permanecer lo más abstracto posible en el nivel API: si, por ejemplo, un método que está programando debe funcionar en mapas, entonces es suficiente declarar un parámetro como Mapa en lugar del tipo HashMap más estricto (porque menos abstracto) . De esa forma, el consumidor de su API puede ser flexible sobre qué tipo de implementación de Mapa quiere pasar a su método.
fuente
Agregando a la respuesta más votada y muchas más arriba destacando el "más genérico, mejor", me gustaría cavar un poco más.
Map
es el contrato de estructura, mientras queHashMap
es una implementación que proporciona sus propios métodos para tratar diferentes problemas reales: cómo calcular el índice, cuál es la capacidad y cómo incrementarlo, cómo insertarlo, cómo mantener el índice único, etc.Veamos el código fuente:
En
Map
tenemos el método decontainsKey(Object key)
:JavaDoc:
Requiere sus implementaciones para implementarlo, pero el "cómo" está en libertad, solo para garantizar que regrese correctamente.
En
HashMap
:Resulta que
HashMap
usa el código hash para probar si este mapa contiene la clave. Por lo tanto, tiene el beneficio del algoritmo hash.fuente
Creas los mismos mapas.
Pero puedes llenar la diferencia cuando la uses. Con el primer caso, podrá usar métodos especiales de HashMap (pero no recuerdo a nadie realmente útil), y podrá pasarlo como un parámetro de HashMap:
fuente
Map es interfaz y Hashmap es una clase que implementa Map Interface
fuente
Map es la interfaz y Hashmap es la clase que implementa eso.
Entonces en esta implementación creas los mismos objetos
fuente
HashMap es una implementación de Map, por lo que es bastante similar pero tiene el método "clone ()" como veo en la guía de referencia))
fuente
En primer lugar
Map
es una interfaz que tiene una implementación diferente como -HashMap
,TreeHashMap
,LinkedHashMap
etc interfaz funciona como una superclase de la clase que implementa. Entonces, de acuerdo con la regla de OOP, cualquier clase concreta que implemente tambiénMap
es unaMap
. Eso significa que podemos asignar / poner cualquierHashMap
variable de tipo a unaMap
variable de tipo sin ningún tipo de conversión.En este caso se puede asignar
map1
amap2
sin ningún tipo de fundición o cualquier perdida de datos -fuente