7

私はここに答えを投稿しました。ここで、読み取りputIfAbsent方法の使用を示すコードは次のとおりです。ConcurrentMap

ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong> ();

public long addTo(String key, long value) {
  // The final value it became.
  long result = value;
  // Make a new one to put in the map.
  AtomicLong newValue = new AtomicLong(value);
  // Insert my new one or get me the old one.
  AtomicLong oldValue = map.putIfAbsent(key, newValue);
  // Was it already there? Note the deliberate use of '!='.
  if ( oldValue != newValue ) {
    // Update it.
    result = oldValue.addAndGet(value);
  }
  return result;
}

このアプローチの主な欠点は、使用するかどうかに関係なく、マップに配置する新しいオブジェクトを作成する必要があることです。オブジェクトが重い場合、これは大きな影響を与える可能性があります。

これはLambdasを使用する機会になると思いました。Java 8 n'をダウンロードしていません。または、公式(会社のポリシー)になるまでダウンロードできますので、これをテストすることはできませんが、このようなものは有効で効果的ですか?

public long addTo(String key, long value) {
  return map.putIfAbsent( key, () -> new AtomicLong(0) ).addAndGet(value);
}

ラムダを使用しnew AtomicLong(0)て、マップに存在しないために作成する必要があると実際に判断されるまで、の評価を遅らせることを望んでいます。

ご覧のとおり、これははるかに簡潔で機能的です。

基本的に、私の質問は次のとおりだと思います。

  1. これは機能しますか?
  2. または、ラムダを完全に誤解しましたか?
  3. いつかこのようなことがうまくいくでしょうか?
4

4 に答える 4

8

更新2015-08-01

以下computeIfAbsentに説明するメソッドは、実際にJavaSE8に追加されています。セマンティクスは、プレリリースバージョンに非常に近いようです。

さらに、computeIfAbsent新しいデフォルトのメソッドの山全体とともに、Mapインターフェイスに追加されました。もちろん、マップは一般にアトミック更新をサポートできませんが、新しいメソッドはAPIにかなりの利便性を追加します。


あなたがやろうとしていることはかなり合理的ですが、残念ながら、現在のバージョンのでは機能しませんConcurrentMap。ただし、機能強化は進行中です。並行性ライブラリの新しいバージョンにConcurrentHashMapV8は、新しいメソッドを含むものが含まれていますcomputeIfAbsent。これにより、あなたが探していることを正確に行うことができます。この新しい方法を使用すると、例を次のように書き直すことができます。

public long addTo(String key, long value) {
    return map.computeIfAbsent( key, () -> new AtomicLong(0) ).addAndGet(value);
}

の詳細については、concurrency-interestメーリングリストにあるConcurrentHashMapV8DougLeaの最初の発表スレッドを参照してください。スレッドの下のいくつかのメッセージは、あなたがやろうとしていることに非常に似た例を示すフォローアップメッセージです。(ただし、古いラムダ構文に注意してください。そのメッセージは結局2011年8月のものです。)そして、ここに最近のjavadocがありConcurrentHashMapV8ます。

この作業はJava8に統合することを目的としていますが、私が見る限りではまだです。また、これはまだ進行中の作業であり、名前や仕様が変更される可能性があります。

于 2013-02-16T17:59:36.460 に答える
2

残念ながら、それはそれほど簡単ではありません。スケッチしたアプローチには、主に2つの問題があります。1。マップのタイプをからに変更する必要がありますMap<String, AtomicLong>Map<String, AtomicLongFunction>引数AtomicLongFunctionをとらずにを返す単一のメソッドを持つ関数インターフェイスがありますAtomicLong)。2.マップから要素を取得するときは、その要素から抜け出すために毎回関数を適用する必要がありますAtomicLong。これにより、取得するたびに新しいインスタンスが作成されることになりますが、これはおそらくあなたが望んでいたものではありません。

ただし、不足している値を埋めるためにオンデマンドで関数を実行するマップを用意するというアイデアは良いものです。実際、GoogleのGuavaライブラリにはまさにそれを実行するマップがあります。彼らのMapMakerを参照してください。実際、そのコードはJava8ラムダ式の恩恵を受けるでしょう:代わりに

   ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .weakKeys()
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });

あなたは書くことができるでしょう

   ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .weakKeys()
       .makeComputingMap((Key key) -> createExpensiveGraph(key));

また

   ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .weakKeys()
       .makeComputingMap(this::createExpensiveGraph);
于 2013-02-14T14:36:00.193 に答える
2

AtomicLong本当に重い物ではありません。より重いオブジェクトの場合、レイジープロキシを検討し、必要に応じてオブジェクトを作成するためにそのプロキシにラムダを提供します。

class MyObject{
    void doSomething(){}
}

class MyLazyObject extends MyObject{
    Funktion create;
    MyLazyObject(Funktion create){
        this.create = create;
    }
    MyObject instance;
    MyObject getInstance(){
        if(instance == null)
            instance = create.apply();
        return instance;
    }
    @Override void doSomething(){getInstance().doSomething();}
}

public long addTo(String key, long value) {
  return map.putIfAbsent( key, new MyLazyObject( () -> new MyObject(0) ) );
}
于 2013-02-14T14:58:45.150 に答える
2

Java 8を使用する場合、値ConcurrentHashMapを持つ必要はまったくないことに注意してください。ConcurrentHashMap.mergeAtomicLongを安全に使用できます:

ConcurrentMap<String, Long> map = new ConcurrentHashMap<String, Long>();

public long addTo(String key, long value) {
    return map.merge(key, value, Long::sum);
}

これははるかに単純で、大幅に高速です。

于 2015-08-02T04:06:41.020 に答える