metodos - ¿Cuál es la diferencia entre los objetos HashMap y Map en Java?
map string string java (13)
¿Cuál es la diferencia entre los siguientes mapas que creo (en otra pregunta, las personas respondieron usándolos de forma indistinta y me pregunto si / cómo son diferentes):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
Agregando a la respuesta más votada y muchas de las anteriores, enfatizando "más genérico, mejor", me gustaría profundizar un poco más.
Map
es la estructura del contrato, mientras que HashMap
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 incrementarla, cómo insertar, cómo mantener el índice único, etc.
Veamos el código fuente:
En Map
tenemos el método de containsKey(Object key)
:
boolean containsKey(Object key);
JavaDoc:
booleano java.util.Map.containsValue (valor del objeto)
Devuelve verdadero si este mapa asigna una o más claves al valor especificado. Más formalmente, devuelve verdadero si y solo si este mapa contiene al menos una asignación a un valor
v
tal que(value==null ? v==null : value.equals(v))
. Es probable que esta operación requiera un tiempo lineal en el tamaño del mapa para la mayoría de las implementaciones de la interfaz del mapa.Parámetros: valor
Valor cuya presencia en este mapa es para betested.
Devoluciones: verdad
si este mapa mapea una o más claves a la especificada
valueThrows:
ClassCastException - si el valor es de un tipo inapropiado para este mapa (opcional)
NullPointerException: si el valor especificado es nulo y este mapa no permite valores nulos (opcional)
Requiere sus implementaciones para implementarlo, pero el "cómo" está en su libertad, solo para garantizar que se devuelva correctamente.
En HashMap
:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
Resulta que HashMap
utiliza el código hash para probar si este mapa contiene la clave. Así que tiene el beneficio del algoritmo hash.
Como lo señalaron TJ Crowder y Adamski, una referencia es a una interfaz, la otra a una implementación específica de la interfaz. De acuerdo con 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 lo ideal para su solución y necesitaba cambiar la implementación del mapa, aún podría usar el Mapa. Interfaz, y cambiar el tipo de instanciación.
En su segundo ejemplo, la referencia del "mapa" es de tipo Map
, que es una interfaz implementada por HashMap
(y otros tipos de Map
). Esta interfaz es un contrato que dice que el objeto asigna claves a valores y admite varias operaciones (por ejemplo, put
, get
). No dice nada sobre la implementación del Map
(en este caso, un HashMap
).
El segundo enfoque es generalmente preferido, ya que normalmente no querría exponer la implementación del mapa específico a los métodos que usan el Map
o mediante una definición de API.
HashMap es una implementación de Map, por lo que es bastante similar, pero tiene el método "clone ()" que veo en la guía de referencia))
Map is interface y Hashmap es una clase que implementa Map Interface.
Mapa es el tipo estático de mapa, mientras que HashMap es el tipo dinámico de mapa. Esto significa que el compilador tratará su objeto de mapa como uno de tipo Mapa, aunque en tiempo de ejecución, puede apuntar a cualquier subtipo de este.
Esta práctica de programación 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 tan abstracto como sea posible en el nivel de 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 es menos abstracto) . De esa manera, el consumidor de su API puede ser flexible con respecto a qué tipo de implementación de mapas desea transferir a su método.
Mapa es la interfaz y Hashmap es la clase que implementa eso.
Así que en esta implementación creas los mismos objetos.
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 es HashMap<String, Object>
, mientras que en el segundo es Map<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 cualquier código que lo esté utilizando. Si lo declara como HashMap<String, Object>
, tiene que cambiar su contrato si desea cambiar la implementación subyacente.
Ejemplo: Digamos que escribo esta clase:
class Foo {
private HashMap<String, Object> things;
private HashMap<String, Object> moreThings;
protected HashMap<String, Object> getThings() {
return this.things;
}
protected HashMap<String, Object> getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
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 comenzar porque creo que esa es la estructura apropiada para usar al escribir la clase.
Más tarde, Mary escribe el código subclasificándolo. Ella tiene algo que necesita hacer con ambas things
y más things
, así que naturalmente pone eso en un método común, y usa el mismo tipo que usé en getThings
/ getMoreThings
cuando define su método:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Más tarde, decido que, en realidad, es mejor si utilizo TreeMap
lugar de HashMap
en Foo
. Actualizo Foo
, cambiando HashMap
a TreeMap
. Ahora, SpecialFoo
ya no compila, porque SpecialFoo
el contrato: Foo
solía decir que proporcionaba HashMap
s, pero ahora está proporcionando TreeMaps
. Así que tenemos que arreglar SpecialFoo
ahora (y este tipo de cosas pueden pasar a través de un código base).
A menos que tuviera una buena razón para compartir que mi implementación estaba usando un HashMap
(y eso sí ocurre), lo que debería haber hecho fue declarar getThings
y getMoreThings
como simplemente devolver Map<String, Object>
sin ser más específico que eso. De hecho, salvo una buena razón para hacer otra cosa, incluso dentro de Foo
, probablemente debería declarar things
y más things
como Map
, no HashMap
/ TreeMap
:
class Foo {
private Map<String, Object> things; // <== Changed
private Map<String, Object> moreThings; // <== Changed
protected Map<String, Object> getThings() { // <== Changed
return this.things;
}
protected Map<String, Object> getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Tenga en cuenta que ahora estoy usando Map<String, Object>
todos los lugares que puedo, solo siendo específico cuando creo los objetos reales.
Si hubiera hecho eso, entonces María habría hecho esto:
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
... y el cambio de Foo
no hubiera hecho que SpecialFoo
dejara de compilar.
Las interfaces (y las clases básicas) nos permiten revelar solo lo necesario , manteniendo nuestra flexibilidad bajo las coberturas para hacer los cambios que sean apropiados. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un HashMap
, solo HashMap
Map
.
Esta no es una regla ciega, pero en general, la codificación a la interfaz más general será menos frágil que la codificación a algo más específico. Si lo hubiera recordado, no habría creado un Foo
que configurara a Mary para el fracaso con SpecialFoo
. Si Mary hubiera recordado eso, entonces, a pesar de que arruiné a Foo
, habría declarado su método privado con Map
lugar de HashMap
y el cambio de contrato de Foo
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 estar, errar hacia la interfaz menos específica.
Solo iba a hacer esto como un comentario sobre la respuesta aceptada, pero se volvió demasiado funky (odio no tener saltos de línea)
Ah, entonces la diferencia es que, en general, Map tiene ciertos métodos asociados con él. pero hay diferentes formas de crear un mapa, como un HashMap, y estas formas diferentes proporcionan métodos únicos que no todos los mapas tienen.
Exactamente, y siempre desea utilizar la interfaz más general que pueda. Considere ArrayList vs LinkedList. Gran diferencia en la forma en que los usas, pero si usas "Lista" puedes cambiar entre ellos fácilmente.
De hecho, puede reemplazar el lado derecho del inicializador por una declaración más dinámica. Qué tal algo como esto:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
De esta manera, si va a completar la colección con una ordenación por inserción, usará una lista enlazada (una ordenación por inserción en una lista matricial es criminal). Pero si no necesita mantenerla ordenada y solo está agregando, utiliza un 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 el mismo código exacto.
Editar respondiendo al comentario:
En cuanto a su comentario de mapa a continuación, Sí, usando la interfaz de "Mapa" lo restringe a solo esos métodos a menos que devuelva la colección de Mapa a HashMap (lo que COMPLETAMENTE elimina el propósito).
A menudo, lo que hará es crear un objeto y completarlo utilizando 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 lanzar por el camino, probablemente esté usando la interfaz incorrecta o su código no está suficientemente estructurado. 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 estés echando.
Observe también el aspecto semi-ordenado de los roles indicados por las interfaces. Un LinkedList hace una buena pila o cola, un ArrayList hace una buena pila pero una cola horrible (una vez más, una eliminación causaría un cambio de la lista completa), por lo que LinkedList implementa la interfaz de la cola, ArrayList no.
Tú creas los mismos mapas.
Pero puedes llenar la diferencia cuando la uses. Con el primer caso podrás usar métodos especiales de HashMap (pero no recuerdo a nadie realmente útil), y podrás pasarlo como un parámetro de HashMap:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Map es una interfaz que implementa HashMap . La diferencia es que en la segunda implementación, su referencia al HashMap solo permitirá el uso de las funciones definidas en la interfaz del Mapa, mientras que la primera permitirá el uso de cualquier función pública en el HashMap (que incluye la interfaz del Mapa).
Probablemente tenga más sentido si lees el tutorial de la interfaz de Sun.
Mapa teniendo las siguientes implementaciones,
HashMap
Map m = new HashMap();
Map m = new LinkedHashMap();
LinkedHashMapMap m = new LinkedHashMap();
Tree Map
Map m = new TreeMap();
Map m = new WeakHashMap();
WeakHashMapMap m = new WeakHashMap();
Supongamos que ha creado un método (es solo un código spudo).
public void HashMap getMap(){
return map;
}
Supongamos que los requisitos de proyecto cambian cada vez de la siguiente manera
- El método debe devolver el contenido del mapa: es necesario devolver
HashMap
. - El método debe devolver la clave del mapa en el orden de inserción: es necesario cambiar el tipo de devolución
HashMap
aLinkedHashMap
. - El método debe devolver las claves del mapa en orden ordenado: es necesario cambiar el tipo de devolución
LinkedHashMap
aTreeMap
.
Si su método devuelve clases específicas en lugar de la interfaz del Map
, debe cambiar el tipo de retorno del método getMap()
cada vez.
Pero, si utiliza la función de polimorfismo de java, en lugar de devolver el Map
interfaz de clase específica utilizada, conlleva la reutilización del código y menos impacto si cambia algún requisito.
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
En primer lugar, Map
es una interfaz que tiene diferentes implementaciones como: HashMap
, TreeHashMap
, LinkedHashMap
, etc. La interfaz funciona como una súper clase para la clase implementadora. Entonces, de acuerdo con la regla de OOP, cualquier clase concreta que implemente Map
es también Map
. Eso significa que podemos asignar / poner cualquier variable de tipo HashMap
a una variable de tipo Map
sin ningún tipo de conversión.
En este caso, podemos asignar map1
a map2
sin ningún lanzamiento o pérdida de datos.
map2 = map1