Como atualizar um valor, dada uma chave em um hashmap?


624

Suponha que tenhamos um HashMap<String, Integer>em Java.

Como atualizo (incremento) o valor inteiro da chave de cadeia para cada existência da cadeia que encontro?

Pode-se remover e reinserir o par, mas a sobrecarga seria uma preocupação.
Outra maneira seria colocar o novo par e o antigo seria substituído.

Neste último caso, o que acontece se houver uma colisão de código de hash com uma nova chave que estou tentando inserir? O comportamento correto para uma hashtable seria atribuir um local diferente para ele ou fazer uma lista dele no intervalo atual.

Respostas:


972
map.put(key, map.get(key) + 1);

deve ficar bem. Ele atualizará o valor para o mapeamento existente. Observe que isso usa boxe automático. Com a ajuda de map.get(key)obtermos o valor da chave correspondente, você poderá atualizar com sua exigência. Aqui estou atualizando para aumentar o valor em 1.


21
De fato, essa é a solução corporativa mais robusta e escalável.
— Lavir the Whiolet

12
@Lavir, não é uma solução ruim, mas não vê como é a mais robusta e escalável. Um atomicinteger é muito mais escalável.
— precisa

13
isso assume que a chave existe, certo? Estou recebendo a exceção nullPointer quando isso não acontece.
— 31414 Ian

84
Com o Java 8, isso pode ser facilmente evitado usando getOrDefault, por exemplo:map.put(key, count.getOrDefault(key, 0) + 1);
— Martin

2
@Martin .. map.put (chave, map.getOrDefault (chave, 0) + 1)
— Sathesh

112

Maneira Java 8:

Você pode usar o computeIfPresentmétodo e fornecer a ele uma função de mapeamento, que será chamada para calcular um novo valor com base no valor existente.

Por exemplo,

Map<String, Integer> words = new HashMap<>();
words.put("hello", 3);
words.put("world", 4);
words.computeIfPresent("hello", (k, v) -> v + 1);
System.out.println(words.get("hello"));

Como alternativa, você pode usar o mergemétodo, em que 1 é o valor padrão e a função incrementa o valor existente em 1:

words.merge("hello", 1, Integer::sum);

Além disso, há um conjunto de outros métodos úteis, tais como putIfAbsent, getOrDefault, forEach, etc.


3
Acabei de testar suas soluções. O segundo, aquele com referência ao método, funciona. O primeiro, a expressão lambda, não está funcionando de maneira consistente quando qualquer valor do seu mapa é null(digamos words.put("hello", null);), o resultado ainda nullnão é o 1que eu esperaria.
— Tao Zhang

4
Do Javadoc: "Se o valor da chave especificada estiver presente e não for nulo, tentará calcular um novo mapeamento". Você pode usar compute(), em vez disso, ele manipulará nullvalores também.
— damluar

Eu quero incrementar meu valor em 1. .mergeÉ a minha solução com Integer::sum.
— S_K 02/04

48
hashmap.put(key, hashmap.get(key) + 1);

O método putirá substituir o valor de uma chave existente e criará-lo se não existe.


55
Não, não cria, dá nullPointer Exception.
— 21815 smttsp #

13
O código é uma resposta correta para a pergunta em questão, mas foi postado um ano depois que o mesmo código exato foi postado na resposta aceita. A única coisa que diferencia esta resposta é a de colocar, pode criar uma nova entrada, o que pode, mas não neste exemplo. Se você estiver usando hashmap.get (chave) para uma chave / valor inexistente, você será nulo e quando tentar incrementar, como @smttsp diz que será NPE. -1
— Zach

8
Esta resposta está errada. NullPointerException para chaves inexistentes
— Eleanore

@smttp NullpointterException somente se você não inicializar o valor (como você sabe que você não pode incremento null)
— Mehdi

Duplicação e explicação incorreta ... e a criará se não existir. Você não pode fazer, null + 1pois isso tentará descompactar o arquivo nullem um número inteiro para fazer o incremento.
— AxelH 16/03/19


30

Substitua Integerpor AtomicIntegere chame um dos métodos incrementAndGet/ getAndIncrementnele.

Uma alternativa é agrupar uma classe intem sua própria MutableIntegerclasse que possua um increment()método, você só tem uma preocupação de segurança de thread a resolver ainda.


37
AtomicInteger é um número inteiro mutável, mas embutido. Eu duvido seriamente que escrever seu próprio MutableInteger seja uma idéia melhor.
— Peter Peterrey

Personalizado MutableIntegeré melhor, como AtomicIntegerusos volatile, que tem sobrecarga. Eu usaria em int[1]vez de MutableInteger.
— Oliv #

@ Oliv: não concorrente.
— BalusC

@ BalusC, mas ainda assim, a gravação volátil é mais cara. Invalida caches. Se não houvesse diferença, todas as variáveis ​​seriam voláteis.
— Oliv #

@ Oliv: pergunta menciona explicitamente a colisão de código de hash, portanto a simultaneidade é importante para o OP.
— BalusC

19

Solução de uma linha:

map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);

4
Isso não acrescenta nada de novo às respostas existentes, acrescenta?
— Robert

1
Sim. A resposta marcada correta lançará uma NullPointerException se a chave não existir. Esta solução irá funcionar bem.
— precisa

18

@ A solução de Matthew é a mais simples e terá bom desempenho na maioria dos casos.

Se você precisar de alto desempenho, o AtomicInteger é uma solução melhor, ala @BalusC.

No entanto, uma solução mais rápida (desde que a segurança do encadeamento não seja um problema) é usar TObjectIntHashMap, que fornece um método de incremento (chave) e usa primitivos e menos objetos do que a criação de AtomicIntegers. por exemplo

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");

13

Você pode incrementar como abaixo, mas precisa verificar a existência para que uma NullPointerException não seja lançada

if(!map.containsKey(key)) {
 p.put(key,1);
}
else {
 p.put(key, map.getKey()+1);
}

9

O hash existe (com 0 como valor) ou é "colocado" no mapa no primeiro incremento? Se for "colocado" no primeiro incremento, o código deve se parecer com:

if (hashmap.containsKey(key)) {
    hashmap.put(key, hashmap.get(key)+1);
} else { 
    hashmap.put(key,1);
}

7

Pode ser um pouco tarde, mas aqui estão meus dois centavos.

Se você estiver usando o Java 8, poderá usar o método computeIfPresent . Se o valor da chave especificada estiver presente e não for nulo, ele tentará calcular um novo mapeamento, dada a chave e seu valor mapeado atual.

final Map<String,Integer> map1 = new HashMap<>();
map1.put("A",0);
map1.put("B",0);
map1.computeIfPresent("B",(k,v)->v+1);  //[A=0, B=1]

Também podemos fazer uso de outro método putIfAbsent para colocar uma chave. Se a chave especificada ainda não estiver associada a um valor (ou estiver mapeada para nula), esse método a associará ao valor fornecido e retornará nulo; caso contrário, retornará o valor atual.

No caso do mapa é compartilhado entre threads, então podemos fazer uso de ConcurrentHashMape AtomicInteger . Do documento:

An AtomicIntegeré um valor int que pode ser atualizado atomicamente. Um AtomicInteger é usado em aplicativos como contadores incrementados atomicamente e não pode ser usado como um substituto para um número inteiro. No entanto, esta classe estende Number para permitir acesso uniforme por ferramentas e utilitários que lidam com classes numericamente baseadas.

Podemos usá-los como mostrado:

final Map<String,AtomicInteger> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("A",new AtomicInteger(0));
map2.putIfAbsent("B",new AtomicInteger(0)); //[A=0, B=0]
map2.get("B").incrementAndGet();    //[A=0, B=1]

Um ponto a observar é que estamos invocando getpara obter o valor da chave Be, em seguida, invocando incrementAndGet()seu valor, que é claro AtomicInteger. Podemos otimizá-lo, pois o método putIfAbsentretorna o valor da chave, se já estiver presente:

map2.putIfAbsent("B",new AtomicInteger(0)).incrementAndGet();//[A=0, B=2]

Além disso, se planejarmos usar o AtomicLong , conforme a documentação sob alta contenção, a taxa de transferência esperada do LongAdder será significativamente maior, à custa de um maior consumo de espaço. Verifique também esta pergunta .


5

A solução mais limpa sem NullPointerException é:

map.replace(key, map.get(key) + 1);

5
se a chave não existe, então map.get (chave) vai jogar NPE
— Navi

Sim, isso é verdade
— Sergey Dirin

2

Como não posso comentar algumas respostas devido à menor reputação, publicarei uma solução que apliquei.

for(String key : someArray)
{
   if(hashMap.containsKey(key)//will check if a particular key exist or not 
   {
      hashMap.put(hashMap.get(key),value+1);// increment the value by 1 to an already existing key
   }
   else
   {
      hashMap.put(key,value);// make a new entry into the hashmap
   }
}

1

Use um forloop para incrementar o índice:

for (int i =0; i<5; i++){
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("beer", 100);

    int beer = map.get("beer")+i;
    System.out.println("beer " + beer);
    System.out ....

}

3
Isso apenas sobrescreveria o mapa em cada iteração. Veja a resposta de Mateus para a abordagem correta.
— Leigh


1
Integer i = map.get(key);
if(i == null)
   i = (aValue)
map.put(key, i + 1);

ou

Integer i = map.get(key);
map.put(key, i == null ? newValue : i + 1);

Inteiro é tipos de dados primitivos http://cs.fit.edu/~ryan/java/language/java-data.html , então você precisa removê-lo, fazer algum processo e colocá-lo novamente. se você tiver um valor que não seja do tipo Primitivo, precisará retirá-lo, processá-lo e não precisará colocá-lo novamente no mapa de hash.


1
Obrigado por este trecho de código, que pode fornecer ajuda imediata. Uma explicação adequada melhoraria muito seu valor educacional, mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com perguntas semelhantes, mas não idênticas. Por favor edite sua resposta para adicionar explicação, e dar uma indicação do que limitações e premissas se aplicam.
— precisa

0

Tentar:

HashMap hm=new HashMap<String ,Double >();

NOTA:

String->give the new value; //THIS IS THE KEY
else
Double->pass new value; //THIS IS THE VALUE

Você pode alterar a chave ou o valor no seu hashmap, mas não pode alterar as duas ao mesmo tempo.


0

Use Java8 construído em função 'computeIfPresent'

Exemplo:

public class ExampleToUpdateMapValue {

    public static void main(String[] args) {
        Map<String,String> bookAuthors = new TreeMap<>();
        bookAuthors.put("Genesis","Moses");
        bookAuthors.put("Joshua","Joshua");
        bookAuthors.put("Judges","Samuel");

        System.out.println("---------------------Before----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
        // To update the existing value using Java 8
        bookAuthors.computeIfPresent("Judges", (k,v) -> v = "Samuel/Nathan/Gad");

        System.out.println("---------------------After----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.