web-dev-qa-db-ja.com

Java map.get(key)-put(key)を自動的に実行し、キーが存在しない場合に戻りますか?

私は次のパターンにうんざりしています:

value = map.get(key);
if (value == null) {
    value = new Object();
    map.put(key, value);
}

この例は、多次元構造を表すためにネストされたマップがある場合に記述される追加のコードの表面をほんの少しだけスクラッチします。

これを回避するためにどこかに何かが存在すると確信していますが、グーグルの努力は何の関連性ももたらしませんでした。提案はありますか?

48

Java.util.concurrent.ConcurrentMap 

およびJava 8

Java.util.Map

持っている

putIfAbsent(K key, V value) 

キーにマップされた値を返すか、キーに値がマップされていない場合は指定された値とnullを挿入します。

値の遅延評価が必要な場合は

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
43
Roger Lindsjö

Java 8は Map にNiceメソッドを追加します: computecomputeIfPresentcomputeIfAbsent

必要なものを実現するには:

Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object());
32
iTake

このパターンの問題は、get()がnullを返す場合に使用する値を何らかの方法で定義する必要があることです。

確かにそこにはライブラリーがあり、IIRCはそれを行う新しいコレクションもいくつかありますが、残念ながら、それらがどれだったか覚えていません。

ただし、新しい値を作成する標準的な方法があれば、ユーティリティメソッドを自分で作成することもできます。このような何かが動作する可能性があります:

public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ {
  V value = map.get(key);
  if( value == null ) {
    value = valueClass.newInstance();
    map.put( key, value );
  }   

  return value;
} 

リフレクション例外をスローするか、メソッドでそれらを処理する必要があることに注意してください。さらに、これには引数なしのコンストラクターを提供するvalueClassが必要です。あるいは、使用する必要があるデフォルト値を単に渡すこともできます。

3
Thomas

Eclipse Collections から MutableMap およびgetIfAbsentPut()を使用できます。キーにマップされます。

メソッド参照を使用して、新しいObjectを作成できます。

MutableMap<String, Object> map = Maps.mutable.empty();
Object value = map.getIfAbsentPut("key", Object::new);

または、新しいObjectを直接作成できます。

MutableMap<String, Object> map = Maps.mutable.empty();    
Object value = map.getIfAbsentPut("key", new Object());

最初の例では、キーにマップされている値がない場合にのみオブジェクトが作成されます。

2番目の例では、オブジェクトは関係なく作成されます。

注:私はEclipse Collectionsの寄稿者です。

3

いずれにせよ、マップにデフォルトのデータが存在しない場合、それを取得する必要がある

map.getOrDefault(key, defaultValue);

javadocs

1

EDIT:下記の機能は廃止される予定であり、代わりに CacheBuilder を使用する必要があります。

Guavaライブラリには、「computing map」があります。 MapMaker.makeComputingMap(Function)

Map<String, Object> map = new MapMaker().makeComputingMap(
    new Function<String, Object>() {
      public String apply(Stringt) {
        return new Object();
      }
  });

関数を数回必要とする場合は、それをユーティリティクラスに抽出し、次にこのようなマップを作成します(ここでMyFunctions.NEW_OBJECTは静的なFunctionインスタンスです):

Map<String, Object> map = new MapMaker()
    .makeComputingMap(MyFunctions.NEW_OBJECT);
1

問題全体を見ているわけではないかもしれませんが、継承または合成を使用してこの動作をMapオブジェクトに追加する方法はありますか?

0
Trein