Qual é a diferença entre os seguintes mapas que eu crio (em outra pergunta, as pessoas responderam usando-os aparentemente de forma intercambiável e eu estou pensando se / como eles são diferentes):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
java
dictionary
hashmap
Tony Stark
fonte
fonte
Respostas:
Não há diferença entre os objetos; você tem um
HashMap<String, Object>
nos dois casos. Há uma diferença na interface que você tem para o objeto. No primeiro caso, a interface éHashMap<String, Object>
, enquanto no segundo éMap<String, Object>
. Mas o objeto subjacente é o mesmo.A vantagem de usar
Map<String, Object>
é que você pode alterar o objeto subjacente para ser um tipo diferente de mapa sem quebrar seu contrato com qualquer código que o esteja usando. Se você o declarar comoHashMap<String, Object>
, precisará alterar seu contrato se desejar alterar a implementação subjacente.Exemplo: Digamos que eu escreva esta classe:
A classe possui alguns mapas internos de string-> objeto que ele compartilha (por meio de métodos acessadores) com subclasses. Digamos que eu escreva com
HashMap
s para começar, porque acho que essa é a estrutura apropriada para usar ao escrever a classe.Mais tarde, Maria escreve código subclassificando-o. Ela tem algo que precisa fazer com ambos
things
emoreThings
, portanto, naturalmente, coloca isso em um método comum, e ela usa o mesmo tipo que eu useigetThings
/getMoreThings
ao definir seu método:Mais tarde, eu decido que na verdade, é melhor se eu usar
TreeMap
em vez deHashMap
noFoo
. Eu atualizoFoo
, mudandoHashMap
paraTreeMap
. Agora,SpecialFoo
não compila mais, porque eu quebrei o contrato:Foo
costumava dizer que forneciaHashMap
s, mas agora está fornecendoTreeMaps
. Portanto, temos que corrigirSpecialFoo
agora (e esse tipo de coisa pode se espalhar por uma base de código).A menos que eu tenha realmente um bom motivo para compartilhar que minha implementação estava usando um
HashMap
(e isso acontece), o que eu deveria ter feito era declarargetThings
egetMoreThings
apenas retornarMap<String, Object>
sem ser mais específico do que isso. De fato, exceto por uma boa razão para fazer outra coisa, mesmo dentro deFoo
eu provavelmente deveria declararthings
emoreThings
comoMap
, nãoHashMap
/TreeMap
:Observe como agora estou usando em
Map<String, Object>
todos os lugares que posso, apenas sendo específico quando crio os objetos reais.Se eu tivesse feito isso, Mary teria feito isso:
... e mudar
Foo
não teria feitoSpecialFoo
parar de compilar.As interfaces (e classes de base) nos permitem revelar apenas o necessário , mantendo nossa flexibilidade oculta para fazer as alterações necessárias. Em geral, queremos que nossas referências sejam o mais básicas possível. Se não precisamos saber que é um
HashMap
, basta chamá-lo deMap
.Essa não é uma regra cega, mas, em geral, a codificação para a interface mais geral será menos frágil do que a codificação para algo mais específico. Se eu tivesse me lembrado disso, não teria criado uma
Foo
que deixasse Mary com defeitoSpecialFoo
. Se Mary tivesse se lembrado disso, mesmo que eu erreiFoo
, ela teria declarado seu método privado emMap
vez deHashMap
e meuFoo
contrato de mudança não teria afetado seu código.Às vezes você não pode fazer isso, às vezes você precisa ser específico. Mas, a menos que você tenha um motivo para estar, erre na interface menos específica.
fonte
Map é uma interface que o HashMap implementa. A diferença é que, na segunda implementação, sua referência ao HashMap permitirá apenas o uso das funções definidas na interface do Mapa, enquanto a primeira permitirá o uso de quaisquer funções públicas no HashMap (que inclui a interface do Mapa).
Provavelmente fará mais sentido se você ler o tutorial de interface da Sun
fonte
O mapa tem as seguintes implementações:
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Mapa da Árvore
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Suponha que você tenha criado um método (este é apenas pseudocódigo).
Suponha que os requisitos do seu projeto sejam alterados:
HashMap
.HashMap
paraLinkedHashMap
.LinkedHashMap
paraTreeMap
.Se o seu método retornar classes específicas em vez de algo que implemente a
Map
interface, você deverá alterar o tipo de retorno dogetMap()
método a cada vez.Mas se você usar o recurso de polimorfismo do Java e, em vez de retornar classes específicas, usar a interface
Map
, ele melhora a reutilização do código e reduz o impacto das alterações de requisitos.fonte
Eu ia fazer isso como um comentário sobre a resposta aceita, mas ela ficou muito divertida (eu odeio não ter quebras de linha)
Exatamente - e você sempre deseja usar a interface mais geral possível. Considere ArrayList vs LinkedList. Diferença enorme na maneira como você os utiliza, mas se você usar a "Lista", poderá alternar entre eles facilmente.
De fato, você pode substituir o lado direito do inicializador por uma instrução mais dinâmica. Que tal algo como isso:
Dessa forma, se você for preencher a coleção com uma classificação de inserção, use uma lista vinculada (uma classificação de inserção em uma lista de matriz é criminosa). Mas se você não precisar mantê-la classificada e apenas anexar, você usa um ArrayList (mais eficiente para outras operações).
Essa é uma extensão bastante grande aqui, porque coleções não são o melhor exemplo, mas no design de OO, um dos conceitos mais importantes é usar a fachada da interface para acessar diferentes objetos com o mesmo código.
Edite respondendo ao comentário:
Quanto ao comentário do seu mapa abaixo, Sim, usando a interface "Mapa" restringe você apenas a esses métodos, a menos que você converta a coleção de Map novamente para o HashMap (que derruba COMPLETAMENTE o objetivo).
Geralmente, o que você faz é criar um objeto e preenchê-lo usando seu tipo específico (HashMap), em algum tipo de método "create" ou "initialize", mas esse método retornará um "Map" 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á estruturado o suficiente. Observe que é aceitável que uma seção do seu código a trate como um "HashMap", enquanto a outra a trate como um "Mapa", mas isso deve fluir "para baixo". para que você nunca esteja lançando.
Observe também o aspecto semi-organizado das funções indicadas pelas interfaces. Um LinkedList cria uma boa pilha ou fila, um ArrayList cria uma boa pilha, mas uma fila horrível (novamente, uma remoção causaria uma mudança na lista inteira), portanto o LinkedList implementa a interface da Fila, o ArrayList não.
fonte
Conforme observado por TJ Crowder e Adamski, uma referência é a uma interface, a outra a uma implementação específica da interface. De acordo com Joshua Block, você deve sempre tentar codificar para interfaces, para permitir lidar melhor com as alterações na implementação subjacente - por exemplo, se o HashMap de repente não era ideal para a sua solução e você precisava alterar a implementação do mapa, você ainda poderia usar o Mapa interface e altere o tipo de instanciação.
fonte
No seu segundo exemplo, a referência "map" é do tipo
Map
, que é uma interface implementada porHashMap
(e outros tipos deMap
). Essa interface é um contrato dizendo que o objeto mapeia chaves para valores e suporta várias operações (por exemploput
,get
). Não diz nada sobre a implementação doMap
(neste caso, aHashMap
).A segunda abordagem é geralmente preferida, pois você normalmente não deseja expor a implementação específica do mapa a métodos que usam a
Map
ou por meio de uma definição de API.fonte
Mapa é o tipo estático de mapa, enquanto HashMap é o tipo dinâmico de mapa. Isso significa que o compilador tratará seu objeto de mapa como sendo do tipo Map, mesmo que em tempo de execução, ele possa apontar para qualquer subtipo dele.
Essa prática de programação em interfaces em vez de implementações tem o benefício adicional de permanecer flexível: por exemplo, você pode substituir o tipo dinâmico de mapa em tempo de execução, desde que seja um subtipo de Map (por exemplo, LinkedHashMap) e alterar o comportamento do mapa em o voo.
Uma boa regra geral é permanecer o mais abstrato possível no nível da API: se, por exemplo, um método que você está programando deve funcionar em mapas, basta declarar um parâmetro como Map em vez do tipo HashMap mais rígido (porque menos abstrato) . Dessa forma, o consumidor da sua API pode ser flexível sobre o tipo de implementação do Mapa que deseja transmitir ao seu método.
fonte
Somando a resposta votada ao topo e muitas respostas acima, enfatizando o "mais genérico, melhor", gostaria de cavar um pouco mais.
Map
é o contrato de estrutura eHashMap
uma implementação que fornece métodos próprios para lidar com diferentes problemas reais: como calcular o índice, qual é a capacidade e como incrementá-lo, como inserir, como manter o índice exclusivo etc.Vamos dar uma olhada no código fonte:
Em
Map
nós temos o método decontainsKey(Object key)
:JavaDoc:
Ele exige suas implementações para implementá-lo, mas o "como fazer" está livre, apenas para garantir que ele retorne correto.
Em
HashMap
:Acontece que
HashMap
usa código hash para testar se este mapa contém a chave. Portanto, ele tem o benefício do algoritmo de hash.fonte
Você cria os mesmos mapas.
Mas você pode preencher a diferença quando a usar. No primeiro caso, você poderá usar métodos HashMap especiais (mas não me lembro de ninguém realmente útil), e poderá passá-lo como um parâmetro HashMap:
fonte
Map é interface e Hashmap é uma classe que implementa Map Interface
fonte
Mapa é a interface e Hashmap é a classe que implementa isso.
Portanto, nesta implementação, você cria os mesmos objetos
fonte
O HashMap é uma implementação do Map, portanto é o mesmo, mas possui o método "clone ()", como eu vejo no guia de referência))
fonte
Primeiro de tudo
Map
é uma interface que tem implementação diferente como -HashMap
,TreeHashMap
,LinkedHashMap
etc. interface funciona como uma super classe para a classe de execução. Portanto, de acordo com a regra da OOP, qualquer classe concreta que implementa tambémMap
é umaMap
. Isso significa que podemos atribuir / colocar qualquerHashMap
variável de tipo a umaMap
variável de tipo sem nenhum tipo de conversão.Nesse caso, podemos atribuir
map1
a elemap2
sem vazamento ou perda de dados -fonte