web-dev-qa-db-ja.com

computeIfPresentを使用する場合のConcurrentHashMap JDK 8

Jdk 8のConcurrent Hash Mapの新しいバージョンには、2つの新しいメソッドがあります。

computeIfAbsent

computeIfPresent

putIfAbsent-古いメソッド

putIfAbsentおよびcomputeIfAbsentの使用例を理解しています。しかし、いつcomputeIfPresentを使用するのか、シナリオはわかりません。また、computeIfPresentがあるのに、なぜputIfAbsentが必要なのですか。 putIfAbsentは、値のインスタンスを少なくとも1つ追加作成します。

理由は、下位互換性を維持するためだけですか?

20
veritas

他の回答で述べたように、新しい「より強力な」メソッドが導入された場合でも、メソッドは常に下位互換性のために保持されます。

computeIfPresentの使用例について:不自然に見えなくても納得できるほど小さい例を見つけるのは難しいかもしれません。一般に、このメソッドの目的は、既存の値を任意の形式でupdateすることです。

1つの例は、(制約付き)単語数の場合があります。特定の単語セットに対して、マップに0の初期数を格納します。次に、単語のシーケンスが処理されます。最初のセットから単語が見つかるたびに、そのカウントが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);
    }
}

(もちろん、このようなことは別の方法で解決することもできますが、これはどちらか一方の形式ではかなり頻繁なタスクであり、新しい方法ではかなり簡潔でエレガントなソリューションが可能になります)

30
Marco13

一般的な使用例は mapscollections で、次のようになります。

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

computeIfAbsent および computeIfPresent は、コレクションへの要素の追加およびコレクションからの要素の削除に非常に便利な操作です。文字列を最初の文字でグループ化する例を次に示します。キーとコレクションの両方が必要なときに作成され、コレクションが空になったときにクリーンアップされることに注意してください。

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

ConcurrentMaps はこれらの操作をアトミックに実行するため、これはマルチスレッド環境で非常に強力です。

例:

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

いくつかの余分な砂糖:

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

文字列のマップから小文字の値をフェッチするnullセーフな方法として、computeIfPresentを使用しました。

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

ComputeIfPresentが利用可能になる前に、私はこれを行わなければなりませんでした:

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

またはこれ:

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

JDKは後方互換性をほとんど壊しません。それは、古いバージョンから最新バージョンのソフトウェアを簡単に移植または実行できないためです。

古いバージョンのライブラリでコンパイルされたソフトウェアを、それらの機能が残っている任意のバージョン(JREがインストールされているユーザー)で実行できます。

0
Thirler