2013-10-09 2 views
40

Je veux vraiment utiliser Map.computeIfAbsent, mais il a été trop longtemps depuis lambdas en premier cycle.Comment utiliser la nouvelle fonction computeIfAbsent?

Presque directement à partir de la documentation: il donne un exemple de l'ancienne façon de faire les choses:

Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>(); 
String key = "snoop"; 
if (whoLetDogsOut.get(key) == null) { 
    Boolean isLetOut = tryToLetOut(key); 
    if (isLetOut != null) 
    map.putIfAbsent(key, isLetOut); 
} 

Et la nouvelle façon:

map.computeIfAbsent(key, k -> new Value(f(k))); 

Mais dans leur exemple, je pense Je ne comprends pas très bien. Comment pourrais-je transformer le code pour utiliser la nouvelle façon de lambda d'exprimer cela?

+0

Je ne sais pas ce que vous ne comprenez pas l'exemple là-bas? –

+2

Qu'est-ce que "k"? Est-ce une variable en cours de définition? Que diriez-vous de "nouvelle valeur" - est-ce quelque chose de java 8, ou représentant un objet que je dois définir ou remplacer? whoLetDogsOut.computeIfAbsent (clé, k -> new Boolean (tryToLetOut (k))) ne compile pas, donc il me manque quelque chose ... –

+0

Qu'est-ce qui ne compile pas exactement? Quelle erreur produit-elle? – axtavt

Répondre

43

Supposons que vous ayez le code suivant:

import java.util.Map; 
import java.util.concurrent.ConcurrentHashMap; 

public class Test { 
    public static void main(String[] s) { 
     Map<String, Boolean> whoLetDogsOut = new ConcurrentHashMap<>(); 
     whoLetDogsOut.computeIfAbsent("snoop", k -> f(k)); 
     whoLetDogsOut.computeIfAbsent("snoop", k -> f(k)); 
    } 
    static boolean f(String s) { 
     System.out.println("creating a value for \""+s+'"'); 
     return s.isEmpty(); 
    } 
} 

Ensuite, vous verrez le message creating a value for "snoop" exactement une fois que le deuxième appel de computeIfAbsent il y a déjà une valeur pour cette clé. Le k dans l'expression lambda k -> f(k) est juste un espace réservé (paramètre) pour la clé que la carte passera à votre lambda pour le calcul de la valeur. Ainsi, dans l'exemple, la clé est passée à l'invocation de la fonction.

Vous pouvez également écrire: whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty()); pour obtenir le même résultat sans méthode d'assistance (mais vous ne verrez pas alors la sortie de débogage). Et encore plus simple, puisqu'il s'agit d'une délégation simple à une méthode existante, vous pouvez écrire: whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty); Cette délégation n'a pas besoin de paramètres à écrire. Pour être plus proche de l'exemple de votre question, vous pouvez l'écrire comme whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key)); (peu importe si vous nommez le paramètre k ou key). Ou écrivez-le comme whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut); si tryToLetOut est static ou whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut); si tryToLetOut est une méthode d'instance.

66

Récemment, je jouais aussi avec cette méthode. J'ai écrit un algorithme memoized pour calcualte les nombres de Fibonacci qui pourraient servir d'illustration supplémentaire sur la façon d'utiliser la méthode.

Nous pouvons commencer par définir une carte et de mettre les valeurs pour les cas de base, à savoir, fibonnaci(0) et fibonacci(1):

private static Map<Integer,Long> memo = new HashMap<>(); 
static { 
    memo.put(0,0L); //fibonacci(0) 
    memo.put(1,1L); //fibonacci(1) 
} 

Et pour l'étape inductive tout ce que nous devons faire est de redéfinir notre fonction de Fibonacci comme suit:

public static long fibonacci(int x) { 
    return memo.computeIfAbsent(x, n -> fibonacci(n-2) + fibonacci(n-1)); 
} 

comme vous pouvez le voir, la méthode computeIfAbsent utilisera l'expression lambda fourni pour calculer le nombre de Fibonacci lorsque le nombre n'est pas présent sur la carte. Cela représente une amélioration significative par rapport à l'algorithme classique, récursif d'arborescence.

+3

Nice, la conversion d'une seule ligne à la programmation dynamique. Très lisse. –

+2

+1, j'aime cette version de Fibonacci: D –

+2

Vous pouvez recevoir moins d'appels récursifs si vous avez l'appel (n-2) en premier? –

13

Un autre exemple. Lors de la construction d'une carte complexe de cartes, la méthode computeIfAbsent() remplace la méthode get() de map. Grâce à l'enchaînement des computeIfAbsent() des appels, des conteneurs manquants sont construits sur la volée par fournies expressions lambda:

// Stores regional movie ratings 
    Map<String, Map<Integer, Set<String>>> regionalMovieRatings = new TreeMap<>(); 

    // This will throw NullPointerException! 
    regionalMovieRatings.get("New York").get(5).add("Boyhood"); 

    // This will work 
    regionalMovieRatings 
    .computeIfAbsent("New York", region -> new TreeMap<>()) 
    .computeIfAbsent(5, rating -> new TreeSet<>()) 
    .add("Boyhood"); 
-8

Il n'y a pas de différence entre l'utilisation computeIfAbsent() et mis simple() get()
fonctions une carte.En d'autres termes, vous pouvez réécrire votre fonction de cette manière

for (char ch : input){ 
      Integer value; 
      if(countMap.containsKey(ch)){ 
       value = countMap.get(ch); 
       value++; 
       countMap.put(ch, value); 
      } 
      else{ 
       value = 1; 
       countMap.put(ch, value); 

      } 
     } 
+0

Quelqu'un peut-il expliquer s'il vous plaît les downvotes? – alexander

4

Ceci est vraiment utile si vous voulez créer un Multimap sans utiliser la bibliothèque de goyave (https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html)

Pour exemple: Si vous souhaitez stocker une liste des étudiants qui se sont inscrits à un sujet particulier. La solution normale pour cette utilisation de la bibliothèque est jdk

Map<String,List<String>> studentListSubjectWise = new TreeMap<>(); 
List<String>lis = studentListSubjectWise.get("a"); 
if(lis == null) { 
    lis = new ArrayList<>(); 
} 
lis.add("John"); 

//continue.... 

Comme il a un code de plaque de la chaudière, les gens ont tendance à utiliser la goyave Mutltimap. En utilisant Map.computeIfAbsent, nous pouvons écrire sur une seule ligne sans goyave Multimap comme suit.

studentListSubjectWise.computeIfAbsent("a", (x -> new ArrayList<>())).add("John"); 

Marks Stuart & Brian Goetz a fait un bon discours sur ce https://www.youtube.com/watch?v=9uTVXxJjuco

Questions connexes