web-dev-qa-db-fra.com

ConcurrentHashMap JDK 8 quand utiliser computeIfPresent

La nouvelle version de Concurrent Hash Map de jdk 8 comporte deux nouvelles méthodes.

computeIfAbsent

computeIfPresent  

putIfAbsent - Ancienne méthode

Je comprends les cas d’utilisation de putIfAbsent et computeIfAbsent . Mais je ne suis pas sûr des scénarios dans lesquels je vais utiliser computeIfPresent . ont computeIfPresent now .putIfAbsent crée au moins une instance supplémentaire de la valeur.

Est-ce que la raison est seulement d'avoir une compatibilité ascendante?

15
veritas

Comme indiqué dans l’autre réponse, les méthodes seront toujours conservées pour des raisons de compatibilité ascendante, même si de nouvelles méthodes plus «puissantes» sont introduites.

Concernant le cas d'utilisation de computeIfPresent: Il peut être difficile de trouver un exemple suffisamment petit pour ne pas sembler artificiel tout en restant convaincant. En général, l’intention de cette méthode est de mettre à jour une valeur existante sous n’importe quelle forme. 

Un exemple pourrait être un (contraint) nombre de mots: pour un ensemble de mots donné, on stocke un nombre initial de 0 dans la carte. Ensuite, une séquence de mots est traitée: chaque fois que l’on trouve un mot dans l’ensemble initial, son nombre est augmenté de 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);
    }
}

(Bien entendu, les problèmes de ce type pourraient être résolus différemment, mais il s'agit d'une tâche assez fréquente sous l'une ou l'autre forme et la nouvelle méthode permet une solution plutôt concise et élégante).

30
Marco13

Un cas d'utilisation courant est maps with collections , comme

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

computeIfAbsent et computeIfPresent sont des opérations très pratiques pour ajouter et supprimer des éléments dans/de la collection. Voici un exemple qui regroupe les chaînes en fonction de leur premier caractère. Notez que les clés et les collections sont créées si nécessaire et nettoyées lorsque la collection est vide:

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

Ceci est extrêmement puissant dans les environnements multithreading car ConcurrentMaps effectue ces opérations de manière atomique.

Exemple:

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

Un peu de sucre supplémentaire:

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, (i, c) -> c.remove(a) && c.isEmpty() ? null : c);
}
3
steffen

J'ai utilisé computeIfPresent comme moyen nullement sécurisé d'extraire des valeurs minuscules d'une carte de chaînes.

String s = fields.computeIfPresent("foo", (k,v) -> v.toLowerCase())

Avant que computeIfPresent soit disponible, je devais le faire:

String s = map.get("foo");
if (s != null) {
    s = s.toLowerCase();
}

Ou ca:

String s = map.containsKey("foo") ? map.get("foo").toLowerCase() : null;
0
austin327

Le JDK ne rompt presque jamais la compatibilité en amont. Parce qu'alors vous ne pouvez pas facilement porter ou exécuter des logiciels d'une version plus ancienne avec la dernière version. 

Vous pouvez exécuter un logiciel compilé avec une version plus ancienne de la bibliothèque avec n’importe quelle version (c’est-à-dire les utilisateurs sur lesquels le JRE est installé) disposant toujours de ces fonctions.

0
Thirler