¿Cómo actualizar un valor, dada una clave en un mapa de hash?

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

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

Uno podría quitar y volver a ingresar al par, pero los gastos generales serían una preocupación.
Otra forma sería simplemente poner el nuevo par y se reemplazaría el anterior.

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.

preguntado el 11 de noviembre de 10 a las 15:11

17 Respuestas

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

debería estar bien. Actualizará el valor del mapeo existente. Tenga en cuenta que esto usa auto-boxing. Con la ayuda de map.get(key) obtenemos el valor de la clave correspondiente, luego puede actualizar con sus requisitos. Aquí estoy actualizando para incrementar el valor en 1.

contestado el 31 de mayo de 20 a las 08:05

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

@Lavir, no es una mala solución, pero no veo cómo es la más robusta y escalable. En cambio, un número entero atómico es mucho más escalable. - juan vint

esto supone que la clave existe, ¿verdad? Recibo una excepción nullPointer cuando no es así. - Ian

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

@Martin .. map.put (clave, map.getOrDefault (clave, 0) + 1) - Sathesh

Forma de Java 8:

Puedes usar computeIfPresent y proporcionarle una función de mapeo, que se llamará para calcular un nuevo valor basado en uno existente.

Por ejemplo, los servicios administrativos de

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, podrías usar merge mé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 muchos otros métodos útiles, como putIfAbsent, getOrDefault, forEach, etc.

Respondido el 11 de Septiembre de 14 a las 10:09

Acabo de probar tus soluciones. El segundo, el que tiene referencia al método, funciona. El primero, la expresión lambda uno, no funciona de manera consistente cuando cualquier valor de su mapa es null (decir words.put("hello", null);), el resultado sigue siendo null no 1 como era de esperar. - tao zhang

De Javadoc: "Si el valor de la clave especificada está presente y no es nulo, intenta calcular una nueva asignación". Puedes usar compute() en cambio, manejará null valores también. - Konstantin Milyutin

Quiero incrementar mi valor en 1. .merge es mi solución con Integer::sum. - S_k

El simplificado Java 8 camino:

map.put(key, map.getOrDefault(key, 0) + 1);

Esto usa el método de HashMap que recupera el valor de una clave, pero si la clave no se puede recuperar, devuelve el valor predeterminado especificado (en este caso, un '0').

Esto es compatible con el núcleo de Java: HashMap getOrDefault (clave de objeto, V defaultValue)

Respondido el 08 de junio de 16 a las 15:06

Este es mejor en caso de que esté en java 1.8 - Hemant Nagpal

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

El método put detectarás reemplazar el valor de una clave existente y la creará si no existe.

Respondido 02 Jul 15, 19:07

No, no crea, da nullPointer Exception. - smttsp

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

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 lo creará si no existe No puedes hacer null + 1 ya que esto intentará desempaquetar el null en un número entero para hacer el incremento. - axelh

Reemplaza Integer by AtomicInteger y llama a uno de los incrementAndGet/getAndIncrement métodos en él.

Una alternativa es envolver un int en su propia MutableInteger clase que tiene un increment() método, solo tiene un problema de seguridad de subprocesos que resolver todavía.

Respondido 24 Jul 15, 17:07

AtomicInteger es un entero mutable pero incorporado. Dudo seriamente que escribir tu propio MutableInteger sea una mejor idea. - pedro laurey

Personalizado MutableInteger es mejor, ya que AtomicInteger usos volatile, que tiene gastos generales. Yo usaría int[1] en lugar de MutableInteger. - oliva

@Oliv: no concurrente. - BalusC

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

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

Solución de una línea:

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

Respondido el 06 de diciembre de 16 a las 18:12

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

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

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

Si necesita 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 subprocesos no sea un problema) es usar TObjectIntHashMap que proporciona un método de incremento (clave) y utiliza primitivas y menos objetos que la creación de AtomicIntegers. p.ej

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

respondido 20 nov., 13:10

Puede incrementar como se muestra a continuación, pero debe verificar la existencia para que no se lance una NullPointerException

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

Respondido el 29 de enero de 15 a las 13:01

¿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);
}

respondido 03 mar '14, 20:03

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

Si está utilizando Java 8, puede hacer uso de computarSiPresente método. Si el valor de la clave especificada está presente y no es nulo, 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 poner si está ausente poner una llave. Si la clave especificada aún no está asociada con un valor (o está asignada a nulo), este método la asocia con el valor dado y devuelve nulo; de lo contrario, devuelve el valor actual.

En caso de que el mapa se comparta entre subprocesos, podemos hacer uso de ConcurrentHashMap y Entero atómico. Del doc:

An AtomicInteger es un valor int que puede actualizarse atómicamente. Un AtomicInteger se usa en aplicaciones como contadores incrementados atómicamente y no se puede usar como reemplazo de un Integer. Sin embargo, esta clase amplía Number para permitir el acceso uniforme de herramientas y utilidades que tratan con 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 observar es que estamos invocando get para obtener el valor de la clave B y luego invocando incrementAndGet() en su valor que es por supuesto AtomicInteger. Podemos optimizarlo como método putIfAbsent devuelve 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 AtómicoLargo luego, según la documentación bajo alta contención, rendimiento esperado de sumador largo es significativamente mayor, a expensas de un mayor consumo de espacio. También revisa esto pregunta.

contestado el 24 de mayo de 16 a las 08:05

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

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

Respondido el 22 de enero de 18 a las 07:01

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

Si eso es verdad - sergey dirin

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
   }
}

Respondido 28 Oct 17, 16:10

Utilizar for bucle 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 ....

}

Respondido el 14 de junio de 12 a las 22:06

Eso solo sobrescribiría el mapa en cada iteración. Vea la respuesta de Mateo para conocer el enfoque correcto. - Leigh

Aquí hay respuestas engañosas a esta pregunta que implican que el método put de Hashtable reemplazará el valor existente si la clave existe, esto no es cierto para Hashtable sino para HashMap. Ver Javadoc para HashMap http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29

Respondido 07 ago 14, 20:08

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

or

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

El entero es tipos de datos primitivos http://cs.fit.edu/~ryan/java/language/java-data.html, por lo que debe sacarlo, hacer un proceso y luego volver a colocarlo. si tiene un valor que no es un tipo de datos primitivo, solo necesita sacarlo, procesarlo, no es necesario volver a colocarlo en el mapa de hash.

Respondido el 25 de Septiembre de 17 a las 01:09

Gracias por este fragmento de código, que puede brindarle ayuda inmediata. Una explicación adecuada mejoraría enormemente su valor educativo al mostrar el porqué Ésta 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 una explicación y dar una indicación de las limitaciones y suposiciones que se aplican. - Toby Speight

Corrección: la clase Integer no es un tipo de datos primitivo, más bien es una clase contenedora para el tipo primitivo int. Además, gracias al autoboxing posterior a Java8, ya hay una respuesta aceptada aquí: stackoverflow.com/a/4158002/10993032 - mikeover

Utilice la función integrada de Java8 '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);
    }
}

Respondido 06 Feb 19, 11:02

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 mapa de hash, pero no puede cambiar ambos al mismo tiempo.

Respondido 18 ago 12, 18:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.