Qual é a diferença entre os objects HashMap e Map em Java?

Qual é a diferença entre os seguintes mapas que eu criei (em outra pergunta, as pessoas responderam usando-os de forma aparentemente intercambiável e eu estou querendo saber se / como eles são diferentes):

HashMap map = new HashMap(); Map map = new HashMap(); 

Não há diferença entre os objects; você tem um HashMap em ambos os casos. Há uma diferença na interface que você tem para o object. No primeiro caso, a interface é HashMap , enquanto no segundo é Map . Mas o object subjacente é o mesmo.

A vantagem de usar Map é que você pode alterar o object subjacente para um tipo diferente de mapa sem quebrar seu contrato com qualquer código que o esteja usando. Se você declará-lo como HashMap , precisará alterar seu contrato se desejar alterar a implementação subjacente.


Exemplo: digamos que eu escreva esta class:

 class Foo { private HashMap things; private HashMap moreThings; protected HashMap getThings() { return this.things; } protected HashMap getMoreThings() { return this.moreThings; } public Foo() { this.things = new HashMap(); this.moreThings = new HashMap(); } // ...more... } 

A class tem alguns mapas internos de string-> object que compartilha (via methods de access) com subclasss. Digamos que eu escreva com o HashMap s para começar, porque eu acho que é a estrutura apropriada para usar ao escrever a class.

Mais tarde, Mary escreve o código subclassificando-o. Ela tem algo que precisa fazer com as things e mais things , então, naturalmente, ela coloca isso em um método comum, e ela usa o mesmo tipo que usei em getThings / getMoreThings ao definir seu método:

 class SpecialFoo extends Foo { private void doSomething(HashMap t) { // ... } public void whatever() { this.doSomething(this.getThings()); this.doSomething(this.getMoreThings()); } // ...more... } 

Mais tarde, decido que, na verdade, é melhor usar o TreeMap vez do HashMap no Foo . Eu atualizo o Foo , mudando o HashMap para o TreeMap . Agora, o SpecialFoo não compila mais, porque eu quebrei o contrato: Foo costumava dizer que ele fornecia o HashMap s, mas agora ele está fornecendo TreeMaps . Então temos que consertar o SpecialFoo agora (e esse tipo de coisa pode passar por uma base de código).

A menos que eu tenha uma boa razão para compartilhar que minha implementação estava usando um HashMap (e isso acontece), o que eu deveria ter feito era declarar getThings e getMoreThings como apenas retornando Map sem ser mais específico do que isso. De fato, salvo uma boa razão para fazer outra coisa, mesmo dentro do Foo eu provavelmente deveria declarar things e mais things como Map , não HashMap / TreeMap :

 class Foo { private Map things; // < == Changed private Map moreThings; // < == Changed protected Map getThings() { // < == Changed return this.things; } protected Map getMoreThings() { // < == Changed return this.moreThings; } public Foo() { this.things = new HashMap(); this.moreThings = new HashMap(); } // ...more... } 

Observe como agora estou usando Map todos os lugares possíveis, sendo específico apenas quando eu criar os objects reais.

Se eu tivesse feito isso, Maria teria feito isso:

 class SpecialFoo extends Foo { private void doSomething(Map t) { // < == Changed // ... } public void whatever() { this.doSomething(this.getThings()); this.doSomething(this.getMoreThings()); } } 

... e mudar o Foo não faria o SpecialFoo parar de compilar.

Interfaces (e classs de base) nos permitem revelar apenas o quanto for necessário , mantendo a nossa flexibilidade sob as capas para fazer as alterações necessárias. Em geral, queremos que nossas referências sejam as mais básicas possíveis. Se não precisarmos saber que é um HashMap , chame-o de Map .

Esta não é uma regra cega, mas em geral, codificar para a interface mais geral será menos frágil do que codificar para algo mais específico. Se eu me lembrei disso, eu não teria criado um Foo que Foo Mary para o fracasso com o SpecialFoo . Se Mary tivesse se lembrado disso, então, mesmo que eu estragasse o Foo , ela teria declarado seu método privado com o Map vez do HashMap e minha troca do contrato do Foo não teria impactado o código dela.

Às vezes você não pode fazer isso, às vezes você tem que ser específico. Mas a menos que você tenha uma razão para ser, erre na direção da interface menos específica.

Mapa é uma interface que o HashMap implementa. A diferença é que, na segunda implementação, sua referência ao HashMap permitirá apenas o uso de funções definidas na interface Map, enquanto a primeira permitirá o uso de quaisquer funções públicas no HashMap (que inclui a interface Map).

Provavelmente fará mais sentido se você ler o tutorial de interface da Sun

Eu só ia fazer isso como um comentário sobre a resposta aceita, mas ficou muito divertido (eu odeio não ter quebras de linha)

ah, então a diferença é que, em geral, o Map tem certos methods associados a ele. mas existem maneiras diferentes de criar um mapa, como um HashMap, e essas maneiras diferentes fornecem methods exclusivos que nem todos os mapas possuem.

Exatamente – e você sempre quer usar a interface mais geral possível. Considere ArrayList vs LinkedList. Enorme diferença em como você os usa, mas se você usar “List” você pode alternar entre eles prontamente.

Na verdade, você pode replace o lado direito do inicializador por uma instrução mais dinâmica. Que tal algo como isso:

 List collection; if(keepSorted) collection=new LinkedList(); else collection=new ArrayList(); 

Desta forma, se você for preencher a coleção com uma ordenação de inserção, você usaria uma linked list (uma sorting de inserção em uma lista de matriz é criminosa.) Mas se você não precisar mantê-la ordenada e estiver apenas anexando, você usa um ArrayList (mais eficiente para outras operações).

Este é um trecho muito grande aqui, porque as collections não são o melhor exemplo, mas no design OO, um dos conceitos mais importantes é usar a fachada da interface para acessar diferentes objects com o mesmo código.

Editar respondendo ao comentário:

Quanto ao seu comentário de mapa abaixo, Sim usando a interface “Map” restringe você somente a esses methods, a menos que você retorne a coleção de Map para HashMap (o que COMPLETAMENTE anula a finalidade).

Muitas vezes, o que você vai fazer é criar um object e preenchê-lo usando seu tipo específico (HashMap), em algum tipo de método “criar” ou “inicializar”, mas esse método retornará um “Mapa” que não precisa ser manipulado como um HashMap mais.

Se você tiver que transmitir pelo caminho, provavelmente está usando a interface errada ou seu código não está bem estruturado. Note que é aceitável ter uma seção do seu código tratando-a como um “HashMap” enquanto a outra a trata como um “Mapa”, mas isso deve fluir “para baixo”. de modo que você nunca está lançando.

Observe também o aspecto semi-limpo das funções indicadas pelas interfaces. Uma LinkedList faz uma boa pilha ou fila, uma ArrayList faz uma boa pilha, mas uma fila horrível (novamente, uma remoção causaria uma mudança na lista inteira), então LinkedList implementa a interface da Fila, o ArrayList não.

insira a descrição da imagem aqui

Mapa tendo implementações seguintes,

  1. Map m = new HashMap(); HashMap Map m = new HashMap();

  2. Map m = new LinkedHashMap(); LinkedHashMap Map m = new LinkedHashMap();

  3. Mapa do Mapa da Árvore Map m = new TreeMap();

  4. Map m = new WeakHashMap(); WeakHashMap Map m = new WeakHashMap();

Suponha que você tenha criado um método (é apenas código de spudo).

 public void HashMap getMap(){ return map; } 

Suponha que as necessidades do projeto estejam mudando a cada vez, como segue,

  1. O método deve retornar o conteúdo do mapa – Precisa retornar o HashMap .
  2. O método deve retornar a chave do mapa em ordem de inserção – É necessário alterar o tipo de retorno HashMap para LinkedHashMap .
  3. O método deve retornar a chave do mapa na ordem classificada – É necessário alterar o tipo de retorno LinkedHashMap para LinkedHashMap .

Se o método retornar Classes específicas em vez da interface Map , você deverá alterar o tipo de retorno do método getMap() cada vez.

Mas, se você usar o recurso de polymorphism do java, em vez de retornar class específica usada interface Map , ele leva a reutilização de código e menos impacto se qualquer requisito for alterado.

Como observado por TJ Crowder e Adamski, uma referência é para uma interface, a outra para uma implementação específica da interface. De acordo com Joshua Block, você deve sempre tentar codificar para interfaces, para permitir que você lide melhor com mudanças na implementação subjacente – ou seja, se o HashMap de repente não fosse ideal para sua solução e você precisasse alterar a implementação do mapa, ainda assim interface e altere o tipo de instanciação.

Em seu segundo exemplo, a referência “map” é do tipo Map , que é uma interface implementada pelo HashMap (e outros tipos de Map ). Essa interface é um contrato dizendo que o object mapeia chaves para valores e suporta várias operações (por exemplo, put , get ). Não diz nada sobre a implementação do Map (neste caso, um HashMap ).

A segunda abordagem é geralmente preferida, pois você normalmente não desejaria expor a implementação específica do mapa a methods usando o Map ou por meio de uma definição de API.

Mapa é o tipo de mapa estático , enquanto HashMap é o tipo dynamic de mapa. Isso significa que o compilador tratará seu object de mapa como sendo do tipo Map, mesmo que, em tempo de execução, ele aponte para qualquer subtipo dele.

Esta prática de programação contra interfaces em vez de implementações tem o benefício adicional de permanecer flexível: você pode, por exemplo, replace o tipo dynamic de mapa em tempo de execução, desde que seja um subtipo de Map (por exemplo, LinkedHashMap) e alterar o comportamento do mapa o voo.

Uma boa regra é permanecer o mais abstrata possível no nível da API: se, por exemplo, um método que você estiver programando tiver que funcionar em mapas, basta declarar um parâmetro como Map em vez do tipo HashMap mais estrito (porque menos abstrato) . Dessa forma, o consumidor de sua API pode ser flexível sobre o tipo de implementação de mapa que deseja passar para seu método.

Você cria os mesmos mapas.

Mas você pode preencher a diferença quando for usá-lo. Com o primeiro caso, você poderá usar methods especiais do HashMap (mas não me lembro de alguém realmente útil), e você poderá passá-lo como um parâmetro do HashMap:

 public void foo (HashMap m1 = ...; Map m2 = ...; foo (m1); foo ((HashMap)m2); 

Somando-se ao topo votado responder e muitos acima destacando o “mais genérico, melhor”, gostaria de cavar um pouco mais.

Map é o contrato de estrutura, enquanto o HashMap é uma implementação que fornece seus próprios methods para lidar com diferentes problemas reais: como calcular o índice, o que é a capacidade e como incrementá-lo, como inserir, como manter o índice exclusivo, etc.

Vamos dar uma olhada no código fonte:

No Map , temos o método de containsKey(Object key) :

 boolean containsKey(Object key); 

JavaDoc:

boolean java.util.Map.containsValue (valor do object)

Retorna verdadeiro se este mapa mapear uma ou mais chaves para o valor especificado. Mais formalmente, retorna true se e somente se este mapa contiver pelo menos um mapeamento para um valor v tal que (value==null ? v==null : value.equals(v)) . Essa operação provavelmente exigirá tempo linear no tamanho do mapa para a maioria das implementações da interface do Mapa.

parameters: value

valor cuja presença neste mapa é para betested

Retorna: true

se este mapa mapear uma ou mais chaves para o especificado

valueThrows:

ClassCastException – se o valor for de um tipo inadequado para este mapa (opcional)

NullPointerException – se o valor especificado for nulo e este mapa não permitir valores nulos (opcional)

Requer suas implementações para implementá-lo, mas o “como” está em sua liberdade, apenas para garantir que ele retorne corretamente.

No HashMap :

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

Acontece que o HashMap usa hashcode para testar se este mapa contém a chave. Por isso, tem o benefício do algoritmo de hash.

Mapa é a interface e Hashmap é a class que implementa isso.

Então, nessa implementação, você cria os mesmos objects

Mapa é interface e Hashmap é uma class que implementa a Interface do Mapa

HashMap é uma implementação do Map então é o mesmo, mas tem o método “clone ()” como eu vejo no guia de referência))

 HashMap map1 = new HashMap(); Map map2 = new HashMap(); 

Primeiro de tudo Map é uma interface que tem diferentes implementações como – HashMap , TreeHashMap , LinkedHashMap etc. Interface funciona como uma super class para a class de implementação. Então, de acordo com a regra da OOP, qualquer class concreta que implemente Map é um Map . Isso significa que podemos atribuir / colocar qualquer variável do tipo HashMap a uma variável do tipo Map sem qualquer tipo de conversão.

Neste caso, podemos atribuir map1 para map2 sem qualquer conversão ou perda de dados –

 map2 = map1