2009-08-28 37 views
278

¿Cuál es la diferencia entre los siguientes mapas que creo (en otra pregunta, la gente respondió usarlos aparentemente de manera intercambiable y me pregunto si/cómo son diferentes):¿Cuál es la diferencia entre los objetos HashMap y Map en Java?

HashMap<String, Object> map = new HashMap<String, Object>(); 
Map<String, Object> map = new HashMap<String, Object>(); 

Respuesta

329

No hay diferencia entre los objetos; tiene 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 declara como HashMap<String, Object>, debe cambiar su contrato si desea cambiar la implementación subyacente.


Ejemplo: Supongamos 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> 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 tiene que ver tanto con things y moreThings, por lo que, naturalmente, ella pone que en un método común, y se utiliza el mismo tipo que utilicé en getThings/getMoreThings la hora de 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, decida que en realidad, es mejor si uso TreeMap en lugar de HashMap en Foo. Actualizo Foo, cambiando HashMap a TreeMap. Ahora, SpecialFoo no compila más, porque he roto el contrato: Foo solía decir que proporcionaba HashMap s, pero ahora proporciona TreeMaps en su lugar. Entonces, tenemos que reparar SpecialFoo ahora (y este tipo de cosas puede afectar a través de una base de código).

menos que tuviera una buena razón para compartir que mi aplicación se utiliza una HashMap (y eso sucede), lo que debería haber hecho era declaran getThings y getMoreThings como se acaba de regresar Map<String, Object> sin ser más específico que eso.De hecho, salvo una buena razón para hacer algo más, 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... 
} 

Nota cómo estoy ahora usando Map<String, Object> todas partes puedo, sólo se 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 cambiando Foo no habría hecho SpecialFoo parada de compilación.

Las interfaces (y las clases base) nos permiten revelar solo tanto como sea necesario, manteniendo nuestra flexibilidad debajo de las fundas para hacer los cambios que correspondan. En general, queremos que nuestras referencias sean lo más básicas posible. Si no necesitamos saber que es un HashMap, simplemente llámelo como Map.

Esto no es una regla ciega, pero en general, que codifica la interfaz más general va a ser menos frágil que la codificación a algo más específico. Si hubiera recordado eso, no habría creado un Foo que configuró Mary para el fracaso con SpecialFoo. Si María se había acordado que, a continuación, a pesar de que la pata Foo, habría declarado su método privado con Map en lugar de HashMap y mi contrato cambiar Foo 's 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.

+0

por lo que la única diferencia es cuando lo paso como un parámetro, por ejemplo, entonces necesito hacer referencia a uno como Map y el otro como HashMap pero son de hecho el mismo tipo exacto de objeto? – sepiroth

+0

Sí, son exactamente el mismo objeto, se trata del * contrato * que está formando con cualquier código que lo use.Actualicé la respuesta un poco para aclarar. –

+44

ah, entonces la diferencia es que, en general, Map tiene ciertos métodos asociados. pero existen diferentes formas de crear un mapa, como HashMap, y estas formas diferentes proporcionan métodos únicos que no tienen todos los mapas. Entonces, si utilizo un mapa, solo puedo usar los métodos Map, pero tengo un HashMap debajo para que se vean los beneficios de velocidad, los beneficios de búsqueda, etc. de HashMap en el mapa. Y si utilicé un HashMap, podría usar esos métodos específicos de HashMap y si finalmente necesito cambiar el tipo de mapa, es mucho más trabajo. – sepiroth

3

crear el mismo mapa.

Pero puede completar la diferencia cuando la use. Con el primer caso, usted será capaz de utilizar métodos especiales HashMap (pero no recuerdo que nadie realmente útil), y serás capaz de pasar como parámetro HashMap:

public void foo (HashMap<String, Object) { ... } 

... 

HashMap<String, Object> m1 = ...; 
Map<String, Object> m2 = ...; 

foo (m1); 
foo ((HashMap<String, Object>)m2); 
8

En el segundo ejemplo, la referencia de "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). Dice nada sobre la implementación del Map (en este caso, un HashMap).

Generalmente, se prefiere el segundo enfoque, ya que normalmente no se desea exponer la implementación del mapa específico a los métodos que usan el Map o mediante una definición de API.

1

El mapa es la interfaz y Hashmap es la clase que implementa eso.

Así que en esta aplicación se crean los mismos objetos

12

Como se ha señalado por TJ Crowder y Adamski, una referencia es a una interfaz, el otro a una aplicació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 necesitaba cambiar la implementación del mapa, aún podría usar el Mapa. interfaz, y cambie el tipo de instanciación.

51

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

Será probablemente más sentido si se lee Sun's interface tutorial

+0

Supongo que: first = HashMap map = new HashMap (); – OneWorld

+0

Es similar a la frecuencia con la que se implementa una lista como ArrayList – Gerard

8

Mapa es la tipo estático del mapa, mientras que HashMap es el tipo dinámico del mapa. Esto significa que el compilador tratará su objeto de mapa como uno de tipo Map, 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 la ventaja adicional de ser 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 cambie el comportamiento del mapa sobre la marcha.

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 trabajar en mapas, es suficiente declarar un parámetro como Mapa en lugar de más estricto (porque menos resumen) Tipo de HashMap. De esta forma, el consumidor de su API puede ser flexible sobre qué tipo de implementación de Map quiere transferir a su método.

17

sólo iba a hacer esto como un comentario sobre la respuesta aceptada, sino que se pusiera demasiado cobarde (Odio no tener saltos de línea)

ah, lo que la diferencia es que en en general, mapa tiene ciertos métodos asociados a él. pero existen formas diferentes o la creación de un mapa, como como HashMap, y estas formas diferentes proporcionan métodos únicos que no todos los mapas tienen.

Exactamente, y siempre quiere usar la interfaz más general que pueda. Considere ArrayList vs LinkedList. Gran diferencia en cómo los usa, pero si usa "Lista" puede cambiar entre ellos fácilmente.

De hecho, puede reemplazar el lado derecho del inicializador con 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 usted va a llenar en la colección con un tipo de inserción, se utiliza una lista enlazada (un tipo de inserción en una lista de arreglo es criminal.) Pero si no es necesario mantenerlo ordenado y solo se agrega, utiliza 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 utilizar la fachada de interfaz para acceder a diferentes objetos con el mismo código exacto.

Editar responder al comentario:

cuanto a su mapa de comentarios a continuación, Sí utilizando el "mapa" de la interfaz se restringe sólo a los métodos a menos que tire la colección de vuelta de Mapa de HashMap (lo que contradice por completo la propósito).

A menudo lo que harás es crear un objeto y rellenarlo 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 lo hace Necesito manipularlo como HashMap.

Si alguna vez tiene que echar el guante por el camino, probablemente esté utilizando la interfaz incorrecta o su código no está lo 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 estés lanzando.

Observe también el aspecto semi-aseado 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 (de nuevo, una eliminación causaría un desplazamiento de la lista completa), por lo que LinkedList implementa la interfaz Queue, ArrayList no lo hace.

+0

pero en este ejemplo, solo obtengo los métodos de la clase general de la lista, ¿verdad? independientemente de si lo hago un LinkedList() o un ArrayList()? Es solo que si utilizo Insertion Sort (que imagino que debe ser un método para List que LinkedList y ArrayList get by inheritance) funciona mucho más rápido en LinkedList? – sepiroth

+0

supongo que lo que estoy buscando es si o no cuando digo Map m = new HashMap () my Map m puede usar los métodos específicos de HashMap, o no. Estoy pensando que no? – sepiroth

+0

ah, espera, no, mi mapa m de arriba debe tener los métodos de HashMap. – sepiroth

0

HashMap es una implementación del Mapa por lo que es exactamente lo mismo, pero tiene "clone()" método según veo en guía de referencia))

0
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 superclase para la clase implementadora. Entonces, según 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-map2 sin ningún tipo de fundición o cualquier perdida de datos -

map2 = map1 
16

enter image description here

mapa Tras siguientes implementaciones,

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Mapa del árbol Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

Supongamos que ha creado un método (Es sólo spudo código).

public void HashMap getMap(){ 
    return map; 
} 

Supongamos que proyectas requisito están cambiando cada vez de la siguiente manera,

  1. método debe devolver contenido Mapa - Necesidad de volver HashMap.
  2. El método debe devolver las claves del mapa en el orden de inserción - Necesita cambiar el tipo de devolución HashMap al LinkedHashMap.
  3. El método debe devolver las claves del mapa en orden ordenado - Necesita cambiar el tipo de devolución LinkedHashMap a TreeMap.

Si su método de volver Clases específicas en lugar de Map interfaz tiene que cambiar el tipo de retorno del método getMap() cada vez.

Pero, si utiliza función polimorfismo de java, lugar de devolver clase específica interfaz Map utiliza, conduce la reutilización de código y un menor impacto o ningún cambio requisito.

2

Mapa es la interfaz y Hashmap es una clase que implementa la interfaz del mapa

0

Agregando a la parte superior votado respuesta y muchos los anteriores hincapié en la "más genérico, mejor", me gustaría profundizar un poco más.

Map es el contrato estructura, mientras que HashMap es una aplicación que proporciona sus propios métodos para hacer frente a diferentes problemas reales: cómo calcular el índice, ¿cuál es la capacidad y cómo incrementarlo, cómo insertar, cómo mantener el índice único , etc.

vamos a mirar en el código fuente:

En Map tenemos el método de containsKey(Object key):

boolean containsKey(Object key); 

Java Doc:

(Object value) booleano java.util.Map.containsValue

Devuelve true si este mapa mapas una o más claves para el 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)). 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 betested

Devuelve: true

si este mapa mapas una o más teclas a las especificadas

valueThrows:

ClassCastException: si el valor es de un tipo inadecuado para este mapa (opcional)

NullPointerException - si el valor especificado es nulo y este mapa no permite valores nulos (opcional)

Se requiere que sus implementaciones de ponerlo en práctica, pero el "cómo" está en su libertad, sólo para garantizar regresa correcto.

En HashMap:

public boolean containsKey(Object key) { 
    return getNode(hash(key), key) != null; 
} 

Resulta que HashMap utiliza código hash para comprobar si este mapa contiene la clave. Por lo tanto, tiene el beneficio del algoritmo hash.

Cuestiones relacionadas