ConcurrentHashMap JDK 8 cuándo usar ComputeIfPresent

Resuelto veritas asked hace 10 años • 3 respuestas

La nueva versión de Concurrent Hash Map de jdk 8 tiene dos nuevos métodos.

calcular si está ausente

calcular si presente

putIfAbsent - Método antiguo

Entiendo los casos de uso de putIfAbsent y ComputeIfAbsent . Pero no estoy seguro de los escenarios en los que usaré ComputeIfPresent . Además, ¿por qué necesito putIfAbsent si tengo ComputeIfPresent ahora? putIfAbsent crea al menos una instancia adicional del valor.

¿La razón es solo para tener compatibilidad con versiones anteriores?

veritas avatar Jul 22 '14 01:07 veritas
Aceptado

Como se menciona en la otra respuesta: los métodos siempre se conservarán para garantizar la compatibilidad con versiones anteriores, incluso si se introducen métodos nuevos y más "poderosos".

Con respecto al caso de uso de computeIfPresent: Puede resultar difícil encontrar un ejemplo que sea lo suficientemente pequeño como para no parecer artificial y aun así ser convincente. En general, la intención de este método es actualizar un valor existente en cualquier forma.

Un ejemplo podría ser un recuento de palabras (restringido) : para un conjunto determinado de palabras, se almacena un recuento inicial 0en el mapa. Luego, se procesa una secuencia de palabras: cada vez que se encuentra una palabra del conjunto inicial, su recuento aumenta en 1:

import java.util.LinkedHashMap;
import java.util.Map;

public class ComputeIfPresentExample 
{
    public static void main(String[] args) 
    {
        Map<String, Integer> wordCounts = new LinkedHashMap<String, Integer>();

        String s = 
            "Lorem ipsum dolor sit amet consetetur iam nonumy sadipscing " + 
            "elitr, sed diam nonumy eirmod tempor invidunt ut erat sed " + 
            "labore et dolore magna dolor sit amet aliquyam erat sed diam";

        wordCounts.put("sed", 0);
        wordCounts.put("erat", 0);

        for (String t : s.split(" "))
        {
            wordCounts.computeIfPresent(t, (k,v) -> v+1);
        }
        System.out.println(wordCounts);
    }
}

(Por supuesto, cosas como esta podrían resolverse de otra manera, pero esta es una tarea bastante frecuente de una forma u otra, y el nuevo método permite una solución bastante concisa y elegante)

Marco13 avatar Jul 21 '2014 19:07 Marco13

Un caso de uso común son los mapas con colecciones , como

Map<String, Collection<String>> strings = new HashMap<>();

computeIfAbsenty computeIfPresentson operaciones muy útiles para agregar y eliminar elementos a/de la colección. Sobre todo porque, a diferencia de put(), los compute*()métodos devuelven el valor actual (ya sea que se haya creado recientemente o no). A continuación se muestra un ejemplo que agrupa cadenas por su primer carácter. Tenga en cuenta que tanto las claves como las colecciones se crean cuando es necesario y se limpian cuando la colección queda vacía:

void addString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfAbsent(index, ign -> new HashSet<>()).add(a);
}

void removeString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfPresent(index, (k, c) -> {
        c.remove(a);
        return c.isEmpty() ? null : c;
    });
}

Ejemplo:

                         // {}
addString("a1");         // {a=[a1]}      <-- collection dynamically created
addString("a2");         // {a=[a1, a2]}
removeString("a1");      // {a=[a2]}
removeString("a2");      // {}            <-- both key and collection removed

Esto es extremadamente poderoso en entornos multiproceso ya que ConcurrentMapsrealiza estas operaciones de forma atómica.

La operación de eliminación puede ser de una sola línea:

void removeString(String a) {
    String index = a.substring(0, 1);
    strings.computeIfPresent(index, (i, c) -> c.remove(a) && c.isEmpty() ? null : c);
}

Así que una vez más en resumen:

Map<String, Set<String>> map = new ConcurrentHashMap<>();
map.computeIfAbsent(key, i -> ConcurrentHashMap.newKeySet()).add(value);
map.computeIfPresent(key, (i, s) -> s.remove(value) && s.isEmpty() ? null : s);
steffen avatar Sep 01 '2016 10:09 steffen