¿Cuál es la diferencia entre los objetos HashMap y Map en Java?
¿Cuál es la diferencia entre los siguientes mapas que creo (en otra pregunta, la gente respondió usándolos aparentemente indistintamente y me pregunto si / cómo son diferentes):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
13 answers
No hay diferencia entre los objetos; tienes un HashMap<String, Object>
en ambos casos. Hay una diferencia en la interfaz que tiene para 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 ningún código que lo esté usando. Si lo declaras como HashMap<String, Object>
, tienes que cambie 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 string->object 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 tiene que ver con ambos things
y moreThings
, así que naturalmente ella pone eso en un método común, y ella usa el mismo tipo que usé en getThings
/getMoreThings
al definir 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 uso TreeMap
en lugar de HashMap
en Foo
. Actualizo Foo
, cambiando HashMap
a TreeMap
. Ahora, SpecialFoo
ya no compila, porque he roto el contrato: Foo
solía decir que proporcionaba HashMap
s, pero ahora está proporcionando TreeMaps
en su lugar. Así que tenemos que arreglar SpecialFoo
ahora (y este tipo de cosas puede ondular a través de una base de código).
A menos que tuviera una muy buena razón para compartir que mi implementación estaba usando un HashMap
(y eso sucede), lo que debería haber hecho era declarar getThings
y getMoreThings
como simplemente regresando 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 moreThings
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 cómo ahora estoy usando Map<String, Object>
en todas partes que puedo, solo siendo específico cuando creo los objetos reales.
Si yo hubiera hecho eso, entonces María habría hecho esto: {[49]]}
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...y cambiar Foo
no habría hecho que SpecialFoo
dejara de compilar.
Las interfaces (y las clases base) nos permiten revelar solo tanto como sea necesario, manteniendo nuestra flexibilidad bajo las cubiertas para hacer cambios según sea apropiado. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un HashMap
, simplemente llámelo un Map
.
Esto no es una regla ciega, pero en general, codificar a la interfaz más general va a ser menos frágil que codificar a algo más específico. Si hubiera recordado eso, no habría creado un Foo
que preparara a Mary para el fracaso con SpecialFoo
. Si Mary hubiera recordado eso, entonces a pesar de que metí la pata Foo
, ella habría declarado su método privado con Map
en lugar de HashMap
y mi cambio del contrato de Foo
no la habría impactado codificar.
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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-20 08:25:12
Map es una interfaz que HashMap implementa. La diferencia es que en la segunda implementación su referencia al HashMap solo permitirá el uso de funciones definidas en la interfaz del Mapa, mientras que la primera permitirá el uso de cualquier función pública en HashMap (que incluye la interfaz del mapa).
Probablemente tendrá más sentido si lees El tutorial de la interfaz de Sun
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-25 10:56:08
Yo solo iba a hacer esto como un comentario sobre la respuesta aceptada, pero se puso demasiado funky (odio no tener saltos de línea)
Ah, entonces la diferencia es que en general, el mapa tiene ciertos métodos asociado con él. pero hay diferentes formas o crear un mapa, tales como un HashMap, y estas diferentes formas proporcionar métodos únicos que no todos los mapas sí.
Exactamente and y siempre quieres usar la interfaz más general que puedas. Considere ArrayList vs LinkedList. Gran diferencia en cómo los usas, pero si usas "Lista" puedes cambiar entre ellos fácilmente.
De hecho, puede reemplazar el lado derecho del inicializador con una instrucción más dinámica. ¿algo como esto:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
De esta manera, si va a llenar la colección con una ordenación de inserción, utilizará una lista vinculada (una ordenación de inserción en una lista de matrices es criminal.) Pero si no necesita mantenerlo ordenado y solo está anexando, use 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 respuesta al comentario:
En cuanto a su comentario de mapa a continuación, Sí, el uso de la interfaz "Mapa" lo restringe a solo esos métodos a menos que vuelva a enviar la colección de Mapa a HashMap (lo que frustra completamente el propósito).
A menudo lo que hará es crear un objeto y rellenarlo usando su tipo específico (HashMap), en algún tipo de método "create" o "initialize", pero ese método devolverá un "Map" que ya no necesita ser manipulado como un HashMap.
Si alguna vez tiene que enviar por cierto, probablemente esté utilizando 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 el otro lo trata como un" Mapa", pero esto debería fluir"hacia abajo". para que nunca hagas el casting.
También observe el aspecto semi-limpio de los roles indicados por las interfaces. Una LinkedList hace una buena pila o cola, un ArrayList hace una buena pila pero un terrible cola (de nuevo, quitar podría causar un desplazamiento de toda la lista) para LinkedList implementa la interfaz de Cola, ArrayList no.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 19:05:34
Map having following implementations,
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Mapa de Árbol
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Supongamos que ha creado un método (es solo código spudo).
public void HashMap getMap(){
return map;
}
Supongamos que los requisitos del 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 las claves del mapa en orden de inserción - Es necesario cambiar el tipo de retorno
HashMap
aLinkedHashMap
. - El método debe devolver las claves del mapa en orden ordenado - Es necesario cambiar el tipo de retorno
LinkedHashMap
aTreeMap
.
Si su método devuelve clases específicas en lugar de la interfaz Map
, debe cambiar el tipo de retorno del método getMap()
cada vez.
Pero, si utiliza polimorfismo característica de java, en lugar de devolver la clase específica utilizada interfaz Map
, Conduce a la reutilización del código y menos impacto si cualquier cambio de requisito.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-16 10:19:26
Como señalan 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 interfaces, para permitirle manejar mejor los cambios en la implementación subyacente, es decir, si HashMap repentinamente no era ideal para su solución y necesitaba cambiar la implementación del mapa, aún podría usar la interfaz del mapa y cambiar el tipo de instanciación.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 16:53:00
En su segundo ejemplo, la referencia "map" 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 soporta varias operaciones (p.ej. put
, get
). No dice nada sobre la implementación del Map
(en este caso a HashMap
).
Generalmente se prefiere el segundo enfoque, ya que normalmente no desea exponer la implementación de mapa específica a métodos que utilizan Map
o a través de una definición de API.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 16:51:00
Map es el tipo estáticode map, mientras que HashMap es el tipo dinámico de map. Esto significa que el compilador tratará su objeto map como uno de tipo Map, aunque en tiempo de ejecución, puede apuntar a cualquier subtipo del mismo.
Esta práctica de programación contra interfaces en lugar de implementaciones tiene el beneficio añadido de permanecer flexible: Puede, por ejemplo, reemplazar el tipo dinámico de mapa en tiempo de ejecución, siempre y cuando sea un subtipo de mapa (por ejemplo, LinkedHashMap), y cambia el comportamiento del mapa sobre la marcha.
Una buena regla general es permanecer lo más abstracto 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 menos abstracto). De esa manera, el consumidor de su API puede ser flexible sobre qué tipo de implementación de mapas desea pasar a su método.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 17:03:05
Se crean los mismos mapas.
Pero puedes rellenar la diferencia cuando la utilices. Con el primer caso podrás usar métodos HashMap especiales (pero no recuerdo a nadie realmente útil), y podrás pasarlo como un parámetro HashMap:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 16:56:34
Añadiendo a la respuesta votada arriba y a muchas anteriores haciendo hincapié en el "más genérico, mejor", me gustaría profundizar un poco más.
Map
es el contrato de estructura mientras que HashMap
es una implementación que proporciona sus propios métodos para lidiar con diferentes problemas reales: cómo calcular el índice, cuál es la capacidad y cómo aumentarla, 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:
Java booleano.útil.Asignar.containsValue (Valor del objeto)
Devuelve true si este mapa asigna una o más claves al valor especificado. Más formalmente, devuelve true 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))
. Esta operación probablemente requerirá 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
Devuelve: true
Si este mapa asigna una o más claves a la
El valor aumenta:
ClassCastException - si el valor es de tipo inadecuado para este mapa (opcional)
NullPointerException-si el valor especificado es null y este mapa no permite valores null (opcional)
Requiere sus implementaciones para implementarlo, pero el" cómo " está en su libertad, solo para garantizar devuelve correcto.
En HashMap
:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
Resulta que HashMap
usa hashcode para probar si este mapa contiene la clave. Así que tiene el beneficio del algoritmo hash.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-28 08:00:21
Map es la interfaz y Hashmap es la clase que implementa eso.
Así que en esta implementación se crean los mismos objetos
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-28 16:59:20
Map es interfaz y Hashmap es una clase que implementa Map Interfaz
HashMap es una implementación de Map, por lo que es bastante igual, pero tiene el método " clone ()"como veo en la guía de referencia))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-23 12:49:02
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 una implementación diferente como - HashMap
, TreeHashMap
, LinkedHashMap
etc. La interfaz funciona como una súper clase para la clase de implementación. Así que de acuerdo con la regla de OOP cualquier clase concreta que implementa Map
es un Map
también. Eso significa que podemos asignar / poner cualquier variable de tipo HashMap
a una variable de tipo Map
sin ningún tipo de fundición.
En este caso podemos asignar map1
a map2
sin ningún casting o pérdida de datos -
map2 = map1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-09 19:10:08