2013-10-09 8 views
40

Ich möchte sehr gerne Map.computeIfAbsent verwenden, aber es ist zu lange seit Lambdas in undergrad gewesen.Wie verwende ich die neue Funktion computeIfAbsent?

Fast direkt aus der Dokumentation: Es gibt ein Beispiel für die alte Art und Weise, Dinge zu tun:

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

und die neue Art und Weise:

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

Aber in ihrem Beispiel, ich glaube, Ich verstehe es nicht ganz. Wie würde ich den Code umwandeln, um den neuen Lambda-Ausdruck zu verwenden?

+0

Ich bin nicht sicher, was Sie aus dem Beispiel dort nicht verstehen? –

+2

Was ist "k"? Wird eine Variable definiert? Wie wäre es mit "new Value" - ist das etwas aus Java 8, oder ein Objekt, das ich definieren oder überschreiben muss? whoLetDogsOut.ComputeIfAbsent (Schlüssel, k -> new Boolean (tryToLetOut (k))) kompiliert nicht, so fehlt mir etwas ... –

+0

Was genau kompiliert nicht? Welchen Fehler erzeugt es? – axtavt

Antwort

43

Angenommen, Sie den folgenden Code haben:

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

Dann wird die Meldung creating a value for "snoop" genau einmal als auf dem zweiten Aufruf von computeIfAbsent gibt es bereits einen Wert für diesen Schlüssel zu sehen. Die k im Lambda-Ausdruck k -> f(k) ist nur ein Platzhalter (Parameter) für den Schlüssel, den die Karte an Ihr Lambda zur Berechnung des Wertes übergeben wird. Im Beispiel wird der Schlüssel an den Funktionsaufruf übergeben.

Alternativ könnten Sie schreiben: whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());, um das gleiche Ergebnis ohne eine Hilfsmethode zu erreichen (aber Sie werden dann die Debugging-Ausgabe nicht sehen). Und noch einfacher, da es eine einfache Delegation an eine existierende Methode ist, die Sie schreiben könnten: whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty); Diese Delegation benötigt keine zu schreibenden Parameter.

Um näher an das Beispiel in Ihrer Frage zu kommen, könnten Sie es als whoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key)); schreiben (es ist egal, ob Sie den Parameter k oder key nennen). Oder schreiben Sie es als whoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);, wenn tryToLetOutstatic oder whoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut); ist, wenn tryToLetOut eine Instanzmethode ist.

66

Kürzlich spielte ich auch mit dieser Methode. Ich schrieb einen memoisierten Algorithmus, um Fibonacci-Zahlen zu berechnen, die als weitere Illustration zur Verwendung der Methode dienen könnten.

Wir durch die Definition einer Karte und setzen die Werte in der es für die Basisfälle beginnen, nämlich fibonnaci(0) und fibonacci(1):

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

Und für den induktiven Schritt alles, was wir tun müssen, ist unsere Fibonacci-Funktion neu zu definieren wie folgt:

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

wie Sie sehen können, wird das Verfahren computeIfAbsent den bereitgestellten Lambda-Ausdruck verwenden, um die Fibonacci-Zahl zu berechnen, wenn die Zahl nicht in der Karte ist. Dies stellt eine signifikante Verbesserung gegenüber dem herkömmlichen rekursiven Baumalgorithmus dar.

+3

Nice, Single-Line-Konvertierung zu dynamischen Programmierung. Sehr glatt. –

+2

+1, Ich mag diese Fibonacci-Version: D –

+2

Sie können weniger rekursive Anrufe erhalten, wenn Sie den (n-2) Anruf zuerst haben? –

13

Ein anderes Beispiel. Beim Erstellen einer komplexen Karte von Karten ersetzt die Methode computeIfAbsent() die get() -Methode der Karte. Durch Verkettung von computeIfAbsent() zusammen Anrufen, fehlende Container on-the-fly, bereitgestellt durch Lambda-Ausdrücke aufgebaut sind:

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

Es gibt keinen Unterschied zwischen der Verwendung von computeIfAbsent() und einfach put() get()
Funktionen eine Karte.Mit anderen Worten können Sie Ihre Funktion auf diese Weise umschreiben

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

Kann mir bitte jemand die downvotes erklären? – alexander

4

Das ist sehr hilfreich, wenn Sie ohne Verwendung von Guave Bibliothek eine Multimap erstellen möchten (https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html)

Für zB: Wenn Sie eine Liste speichern möchten von Studenten, die sich für ein bestimmtes Fach eingeschrieben haben. Die normale Lösung für diese Verwendung jdk Bibliothek ist

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

//continue.... 

Da es einige Kesselblech Code haben, Menschen Guave Mutltimap verwenden neigen.

Mit Map.computeIfAbsent können wir wie folgt in einer Zeile ohne Guava Multimap schreiben.

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

Stuart Marks & Brian Goetz hat ein gutes Gespräch über dieses https://www.youtube.com/watch?v=9uTVXxJjuco

Verwandte Themen