¿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>();
Author: 030, 2009-08-28

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.

 362
Author: T.J. Crowder,
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

 52
Author: Graphics Noob,
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.

 17
Author: Bill K,
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

introduzca la descripción de la imagen aquí

Map having following implementations,

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Mapa de Árbol Map m = new TreeMap();

  4. 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,

  1. El método debe devolver el contenido del mapa-Es necesario devolver HashMap.
  2. El método debe devolver las claves del mapa en orden de inserción - Es necesario cambiar el tipo de retorno HashMap a LinkedHashMap.
  3. El método debe devolver las claves del mapa en orden ordenado - Es necesario cambiar el tipo de retorno LinkedHashMap a TreeMap.

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.

 16
Author: atish shimpi,
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.

 12
Author: aperkins,
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.

 8
Author: Adamski,
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.

 8
Author: Matthias,
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); 
 3
Author: Roman,
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.

 2
Author: WesternGun,
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

 1
Author: Diego Dias,
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

 1
Author: ,
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-05-09 06:24:26

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))

 0
Author: kolyaseg,
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
 0
Author: Razib,
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