Adicionar um valor duplicado a um HashSet / HashMap substitui o valor anterior

137

Considere o trecho de código abaixo:

HashSet hs = new HashSet();
hs.add("hi"); -- (1)
hs.add("hi"); -- (2)

hs.size()fornecerá 1, pois HashSetnão permite duplicatas; portanto, apenas um elemento será armazenado.

Quero saber se adicionamos o elemento duplicado, então ele substitui o elemento anterior ou simplesmente não o adiciona?

Além disso, o que acontecerá usando HashMappara o mesmo caso?

Anand
fonte

Respostas:

246

No caso de HashMap, ele substitui o valor antigo pelo novo.

No caso de HashSet, o item não está inserido.

Keppil
fonte
1
Não sabe o que está faltando, mas o código fonte parece indicar o contrário? Vejo que eles não fazem uma verificação no suporte HashMappara ver se o keyjá existe antes de chamar puto suporte map?
mystarrocks
10
@ mystarrocks: A chave é o elemento do Set, e isso nunca é substituído pela put()operação.
Keppil
1
ah entendi agora. Eu entendi que a chave é o elemento do Set, mas acabei de perceber que put()apenas substituirá o valor, não a chave. Nesse caso, é o mesmo valor colocado ao lado da chave novamente, o que pode ou não ser melhor do que verificar se a chave existe e coloca. De qualquer maneira, eu entendo como isso funciona.
mystarrocks
Apenas curioso, por que o HashMap e o HashSet escolhem ser assim?
Helin Wang
@HelinWang: Eu não acho que foi planejado, acho que é apenas um efeito de HashSetser implementado na forma de HashMap. Difícil saber, a menos que você seja um dos desenvolvedores das classes.
9134 Keppil
47

A primeira coisa que você precisa saber é que HashSetage como a Set, o que significa que você adiciona seu objeto diretamente ao HashSete não pode conter duplicatas. Você acabou de adicionar seu valor diretamente HashSet.

No entanto, HashMapé um Maptipo. Isso significa que toda vez que você adiciona uma entrada, você adiciona um par de valor-chave.

Em HashMapvocê pode ter valores duplicados, mas não chaves duplicadas. Na HashMapnova entrada substituirá a antiga. A entrada mais recente será na HashMap.

Entendendo o link entre o HashMap e o HashSet:

Lembre-se, HashMapnão pode ter chaves duplicadas. Nos bastidores HashSetusa um HashMap.

Quando você tenta adicionar qualquer objeto a HashSet, essa entrada é realmente armazenada como uma chave no HashMap- o mesmo HashMapque é usado nos bastidores HashSet. Como esse subjacente HashMapprecisa de um par de valor-chave, um valor fictício é gerado para nós.

Agora, quando você tentar inserir outro objeto duplicado no mesmo HashSet, ele tentará novamente inseri-lo como uma chave na parte de HashMapbaixo. No entanto, HashMapnão suporta duplicatas. Conseqüentemente,HashSet ainda resultará em ter apenas um valor desse tipo. Como uma observação lateral, para cada chave duplicada, já que o valor gerado para nossa entrada no HashSet é algum valor aleatório / fictício, a chave não é substituída. ele será ignorado porque remover a chave e adicionar novamente a mesma chave (o valor fictício é o mesmo) não faria nenhum sentido.

Resumo:

HashMappermite duplicar values, mas não keys. HashSetnão pode conter duplicatas.

Para saber se a adição de um objeto foi concluída com êxito ou não, você pode verificar o booleanvalor retornado quando ligar .add() e ver se ele retorna trueou false. Se retornou true, foi inserido.

Jimmy
fonte
HashMap allows duplicate valuesO HashMap substitui o valor antigo pelo novo.
Alex78191
20

Os documentos são bem claros sobre isso: HashSet.add não substitui:

Adiciona o elemento especificado a este conjunto, se ainda não estiver presente. Mais formalmente, adiciona o elemento especificado e a este conjunto se este conjunto não contiver nenhum elemento e2, de modo que (e == null? E2 == null: e.equals (e2)). Se esse conjunto já contiver o elemento, a chamada deixará o conjunto inalterado e retornará falso.

Mas irá substituir:HashMap.put

Se o mapa continha anteriormente um mapeamento para a chave, o valor antigo será substituído.

pb2q
fonte
4

É o caso do HashSet, NÃO o substitui.

Dos documentos:

http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html#add(E )

"Adiciona o elemento especificado a este conjunto se ele ainda não estiver presente. Mais formalmente, adiciona o elemento especificado e a esse conjunto se esse conjunto não contiver nenhum elemento e2, de modo que (e == null? E2 == null: e.equals ( e2)). Se este conjunto já contiver o elemento, a chamada deixará o conjunto inalterado e retornará falso. "

Bob Provencher
fonte
1

Me corrija se eu estiver errado, mas o que você está dizendo é que, com as strings, "Oi" == "Oi" nem sempre se torna realidade (porque elas não são necessariamente o mesmo objeto).

Porém, o motivo pelo qual você está obtendo uma resposta 1 é porque a JVM reutilizará objetos de cadeias sempre que possível. Nesse caso, a JVM está reutilizando o objeto de sequência e, portanto, substituindo o item no Hashmap / Hashset.

Mas você não tem esse comportamento garantido (porque pode ser um objeto de string diferente que tenha o mesmo valor "Oi"). O comportamento que você vê é apenas devido à otimização da JVM.

Nick Rippe
fonte
0

Você precisa verificar o método put no mapa Hash primeiro, pois o HashSet é feito pelo HashMap

  1. Quando você adiciona um valor duplicado, diga uma String "One" no HashSet,
  2. Uma entrada ("one", PRESENT) será inserida no Hashmap (para todos os valores adicionados ao conjunto, o valor será "PRESENT", que se for do tipo Object)
  3. O Hashmap adiciona a entrada no Mapa e retorna o valor, que é neste caso "PRESENT" ou nulo se a Entrada não estiver lá.
  4. O método add do Hashset retorna true se o valor retornado do Hashmap for igual a null, caso contrário false, o que significa que uma entrada já existe ...
shiv
fonte
0

Para dizer de maneira diferente: Quando você insere um par de valores-chave em um HashMap em que a chave já existe (em certo sentido, hashvalue () fornece o mesmo valor e und equal () é verdadeiro, mas os dois objetos ainda podem diferir de várias maneiras ), a chave não é substituída, mas o valor é substituído. A chave é usada apenas para obter o hashvalue () e encontrar o valor na tabela com ele. Como o HashSet usa as chaves de um HashMap e define valores arbitrários que realmente não importam (para o usuário), como resultado, os Elementos do conjunto também não são substituídos.

Marco Rothley
fonte
0

HashMapbasicamente contém o Entryque contém subsequentemente Key(Object)e. Value(Object)Internamente HashSetsão HashMape HashMapsubstituem valores como alguns de vocês já apontaram .. mas ele realmente substitui as chaves ??? Não .. e esse é o truque aqui. HashMapmantém seu valor como chave no subjacente HashMape o valor é apenas um objeto fictício. Portanto, se você tentar reinserir o mesmo valor no HashMap (chave no mapa subjacente). Ele substitui o valor fictício e não a chave (Valor para HashSet).

Veja o código abaixo para a classe HashSet:

public boolean  [More ...] add(E e) {

   return map.put(e, PRESENT)==null;
}

Aqui e é o valor do HashSet, mas a chave do mapa subjacente. E a chave nunca são substituídas. Espero que eu seja capaz de limpar a confusão.

Kunal Kumar
fonte