¿Cómo actualizar un valor, dada una clave en un hashmap?


625

Supongamos que tenemos un HashMap<String, Integer>en Java.

¿Cómo actualizo (incremento) el valor entero de la cadena-clave para cada existencia de la cadena que encuentro?

Uno podría quitar y volver a entrar en el par, pero los gastos generales serían una preocupación.
Otra forma sería simplemente poner el nuevo par y el viejo sería reemplazado.

En el último caso, ¿qué sucede si hay una colisión de código hash con una nueva clave que estoy tratando de insertar? El comportamiento correcto para una tabla hash sería asignarle un lugar diferente o hacer una lista en el depósito actual.

Respuestas:


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

debería estar bien. Actualizará el valor de la asignación existente. Tenga en cuenta que esto utiliza el auto-boxeo. Con la ayuda de map.get(key)obtenemos el valor de la clave correspondiente, luego puede actualizar con su requerimiento. Aquí estoy actualizando para incrementar el valor en 1.


21
De hecho, es la solución empresarial más robusta y escalable.
Lavir el Whiolet

12
@Lavir, no es una mala solución, pero no veo cómo es la más robusta y escalable. Un atomicinteger en cambio es mucho más escalable.
John Vint

13
esto supone que la clave existe, ¿verdad? Obtengo nullPointer Exception cuando no lo hace.
Ian

84
Con Java 8, esto se puede evitar fácilmente utilizando getOrDefault, por ejemplo:map.put(key, count.getOrDefault(key, 0) + 1);
Martin

2
@Martin .. map.put (key, map.getOrDefault (key, 0) + 1)
Sábado

112

Manera Java 8:

Puede usar el computeIfPresentmétodo y proporcionarle una función de mapeo, que se llamará para calcular un nuevo valor basado en uno existente.

Por ejemplo,

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"));

Alternativamente, puede usar el mergemétodo, donde 1 es el valor predeterminado y la función incrementa el valor existente en 1:

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

Además, hay un montón de otros métodos útiles, tales como putIfAbsent, getOrDefault, forEach, etc.


3
Acabo de probar sus soluciones. El segundo, el que tiene referencia de método, funciona. La primera, la expresión lambda, no funciona de manera consistente cuando cualquier valor de su mapa es null(digamos words.put("hello", null);), el resultado aún nullno 1es el que esperaría.
Tao Zhang

44
Desde Javadoc: "Si el valor de la clave especificada está presente y no es nulo, intenta calcular una nueva asignación". Puede usar compute()en su lugar, también manejará nullvalores.
Damluar

Quiero incrementar mi valor en 1. .mergees mi solución con Integer::sum.
S_K

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

El método putse reemplace el valor de una clave existente y se creará si no existe.


55
No, no crea, da nullPointer Exception.
smttsp 01 de

13
El código es una respuesta correcta para la pregunta dada, pero se publicó un año después de que se publicó exactamente el mismo código en la respuesta aceptada. Lo que diferencia esta respuesta es decir que put puede crear una nueva entrada, que puede, pero no en este ejemplo. Si está utilizando hashmap.get (key) para una clave / valor inexistente, obtendrá un valor nulo y cuando intente aumentar, como @smttsp dice que será NPE. -1
Zach

8
Esta respuesta es incorrecta. NullPointerException para claves no existentes
Eleanore

@smttp NullpointterException solo si no inicializó el valor (como sabe, no puede incrementar nulo)
Mehdi

Duplicación y explicación incorrecta ... y la creará si no existe No puede hacerlo null + 1ya que esto intentará desempaquetar nullen un entero para hacer el incremento.
AxelH


30

Reemplace Integerpor AtomicIntegery llame a uno de los métodos incrementAndGet/ getAndIncrementen él.

Una alternativa es envolver un inten su propia MutableIntegerclase que tiene un increment()método, solo tiene una preocupación de seguridad de hilos para resolver todavía.


37
AtomicInteger es un entero mutable pero incorporado. Dudo seriamente que escribir tu propio MutableInteger sea una mejor idea.
Peter Lawrey

La costumbre MutableIntegeres mejor, como AtomicIntegerusos volatile, que tiene gastos generales. Yo usaría en int[1]lugar de MutableInteger.
Oliv

@Oliv: no concurrente.
BalusC

@BalusC pero aún así, la escritura volátil es más costosa. Invalida los cachés. Si no hubiera diferencia, todas las variables serían volátiles.
Oliv

@Oliv: la pregunta menciona explícitamente la colisión de código hash, por lo que la concurrencia es importante para OP.
BalusC

19

Solución de una línea:

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

44
Eso no agrega nada nuevo a las respuestas existentes, ¿verdad?
Robert

1
Sí lo hace. Su respuesta marcada correcta arrojará una NullPointerException si la clave no existe. Esta solución funcionará bien.
Hemant Nagpal

18

La solución de @ Matthew es la más simple y funcionará lo suficientemente bien en la mayoría de los casos.

Si necesita un alto rendimiento, AtomicInteger es una mejor solución ala @BalusC.

Sin embargo, una solución más rápida (siempre que la seguridad de los hilos no sea un problema) es usar TObjectIntHashMap, que proporciona un método de incremento (clave) y usa primitivas y menos objetos que crear AtomicIntegers. p.ej

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

13

Puede aumentar como a continuación, pero debe verificar la existencia para que no se produzca una NullPointerException

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

9

¿Existe el hash (con 0 como valor) o se "pone" en el mapa en el primer incremento? Si se "pone" en el primer incremento, el código debería verse así:

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

7

Puede que sea un poco tarde, pero aquí están mis dos centavos.

Si está utilizando Java 8, puede utilizar el método computeIfPresent . Si el valor para la clave especificada está presente y no es nulo, entonces intenta calcular una nueva asignación dada la clave y su valor asignado actual.

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]

También podemos hacer uso de otro método putIfAbsent para poner una clave. Si la clave especificada no está ya asociada a un valor (o está asignada a nulo), este método la asocia al valor dado y devuelve nulo; de lo contrario, devuelve el valor actual.

En caso de que el mapa se comparte a través de las discusiones a continuación, podemos hacer uso de ConcurrentHashMapy AtomicInteger . Del documento:

Un AtomicIntegeres un valor int que puede actualizarse atómicamente. Un AtomicInteger se usa en aplicaciones tales como contadores incrementados atómicamente, y no se puede usar como reemplazo de un Integer. Sin embargo, esta clase sí extiende el Número para permitir el acceso uniforme de herramientas y utilidades que se ocupan de clases basadas en números.

Podemos usarlos como se muestra:

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]

Un punto a tener en cuenta es que estamos invocando getpara obtener el valor de la clave By luego invocando incrementAndGet()su valor, que es, por supuesto AtomicInteger. Podemos optimizarlo ya que el método putIfAbsentdevuelve el valor de la clave si ya está presente:

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

En una nota al margen si planeamos usar AtomicLong, entonces, según la documentación bajo alta contención, el rendimiento esperado de LongAdder es significativamente mayor, a expensas de un mayor consumo de espacio. También revise esta pregunta .


5

La solución más limpia sin NullPointerException es:

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

55
si la clave no existe, map.get (key) arrojará NPE
Navi

Sí, eso es verdad
Sergey Dirin

2

Como no puedo comentar algunas respuestas debido a la menor reputación, publicaré una solución que apliqué.

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 un forbucle para incrementar el í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
Eso simplemente sobrescribiría el Mapa en cada iteración. Vea la respuesta de Matthew para el enfoque correcto.
Leigh


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

o

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

Entero son tipos de datos primitivos http://cs.fit.edu/~ryan/java/language/java-data.html , por lo que debe extraerlo, realizar algún proceso y luego volver a colocarlo. Si tiene un valor que no son tipos de datos primitivos, solo necesita extraerlo, procesarlo, no es necesario volver a colocarlo en el hashmap.


1
Gracias por este fragmento de código, que puede proporcionar ayuda inmediata. Una explicación adecuada mejoraría en gran medida su valor educativo al mostrar por qué esta es una buena solución al problema y la haría más útil para futuros lectores con preguntas similares, pero no idénticas. Por favor, editar su respuesta para agregar explicación y dar una indicación de lo que se aplican limitaciones y supuestos.
Toby Speight

0

Tratar:

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

Puede cambiar la clave o el valor en su hashmap, pero no puede cambiar ambos al mismo tiempo.


0

Utilice Java8 integrado en la función 'computeIfPresent'

Ejemplo:

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);
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.