145

私は非常にMap.computeIfAbsentを使用したいと思っていますが、学部生のラムダ以来、長すぎます。

ドキュメントからほぼ直接:古い方法の例を示します:

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

そして新しい方法:

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

しかし、彼らの例では、私は完全に「理解」していないと思います。これを表現する新しいラムダ式を使用するには、コードをどのように変換すればよいでしょうか?

4

5 に答える 5

133

最近は私もこの方法で遊んでいます。フィボナッチ数を計算するためのメモ化されたアルゴリズムを作成しました。これは、この方法の使用方法に関する別の例として役立ちます。

マップを定義し、基本ケースの値をそこに入れることから始めることができます。つまり、fibonnaci(0)fibonacci(1):

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

帰納的なステップでは、フィボナッチ関数を次のように再定義するだけです。

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

ご覧のとおり、このメソッドcomputeIfAbsentは、提供されたラムダ式を使用して、数値がマップに存在しない場合にフィボナッチ数を計算します。これは、従来のツリー再帰アルゴリズムを大幅に改善したことを表しています。

于 2013-10-09T21:36:02.597 に答える
116

次のコードがあるとします。

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

その後creating a value for "snoop"、2 回目の呼び出しでcomputeIfAbsentそのキーの値が既に存在するため、メッセージが 1 回だけ表示されます。kラムダ式のk -> f(k)は、値を計算するためにマップがラムダに渡すキーの単なるプレースホルダー (パラメーター) です。したがって、この例では、キーが関数呼び出しに渡されます。

別の方法として、次のように書くこともできます:whoLetDogsOut.computeIfAbsent("snoop", k -> k.isEmpty());ヘルパー メソッドなしで同じ結果を得る (ただし、その場合、デバッグ出力は表示されません)。さらに簡単なことに、これは既存のメソッドへの単純な委譲であるため、記述できます。whoLetDogsOut.computeIfAbsent("snoop", String::isEmpty);この委譲では、パラメータを記述する必要はありません。

質問の例に近づけるために、次のように書くことができますwhoLetDogsOut.computeIfAbsent("snoop", key -> tryToLetOut(key));(パラメーターに名前を付けるkkey. または、 isまたはwhoLetDogsOut.computeIfAbsent("snoop", MyClass::tryToLetOut);ifがインスタンス メソッドであるかのように記述します。tryToLetOutstaticwhoLetDogsOut.computeIfAbsent("snoop", this::tryToLetOut);tryToLetOut

于 2013-10-09T18:12:35.883 に答える
50

もう一つの例。マップの複雑なマップを構築する場合、computeIfAbsent() メソッドはマップの get() メソッドの代わりになります。computeIfAbsent() 呼び出しを連鎖させることにより、欠落しているコンテナーが提供されたラムダ式によってオンザフライで構築されます。

  // 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");
于 2015-01-28T00:14:32.193 に答える