2011-01-28 18 views
32

私のコードにMap<String, List<String>>があります。マップの#get()メソッドがヌルの代わりに空のリストを返した場合、潜在的なヌルポインタを避けることができます。 Java APIにこれと同じものはありますか?私はちょうどHashMapを延長するべきですか?nullの代わりにデフォルト値を返すJavaマップの実装

+7

Decoratorパターンを使用すると、おそらく私はあなたを疑わないのHashMap ... – kem

+1

を拡張するよりも良いだろう、なぜそれが良いのでしょうか? –

+7

@jk:どうしてあなたは自分をHashMapにしたいのですか?時にはLinkedHashMapが必要な場合はどうなりますか?またはConcurrentHashMap?またはTreeMap?基本的には、継承よりも構図を好む:) –

答えて

23

@ Jonの答えは、あなたが直接聞いていることをする良い方法です。

しかし、あなたが実装しようとしているものは "マルチマップ"です。すなわち、キーから値の集合へのマッピングである。その場合は、GuavaまたはApacheのコモンズコレクションのマルチマップクラスも参照する必要があります。

ルックで:

+4

+1キーを含んでいない 'Multimap'の' get() 'は、契約によって空の' Collection'を返します... OPが望むものを正確に返します。不特定の振る舞いを持つApache Commons 'MultiMap'についても同じことが当てはまります。 – ColinD

+2

@ColinD真。 Apacheコモンズを使用する場合、マップに指定された値が含まれていない場合にデフォルト値を返すDefaultedMapを使用できます。 – simao

24

コメントで述べたように:

グアバのコンピューティング・マップの概念はLoadingCacheで取って代わられました。また、Java 8マップのインターフェイスのnice computeIfAbsent地図の契約を破らず、遅延評価機能を備えていないデフォルトのメソッドに導入されました。


Guavaは、それが存在しない場合は、値を提供するために、関数を実行します「コンピューティング・マップ」のアイデアを持っていました。それはMapMaker.makeComputingMapで実装されました。詳細はCacheBuilder.buildを参照してください。CacheBuilderを使用することができます。

それはあなたが後にしている何のためにやり過ぎかもしれ - あなただけMap(組成物を用いてではなく、任意の特定の実装を拡張する)を持っており、それがないならば、単にデフォルトを返すMap実装を記述したほうが良いかもしれませんプレゼント。 get以外のすべてのメソッドは、おそらく他のマップに委任することができます:

public class DefaultingMap<K, V> implements Map<K, V> 
{ 
    private final Map<K, V> map; 
    private final V defaultValue; 

    public DefaultingMap(Map<K, V> map, V defaultValue) 
    { 
     this.map = map; 
     this.defaultValue = defaultValue; 
    } 

    @Override public V get(Object key) 
    { 
     V ret = map.get(key); 
     if (ret == null) 
     { 
      ret = defaultValue; 
     } 
     return ret; 
    } 

    @Override public int size() 
    { 
     return map.size(); 
    } 

    // etc 
} 
+4

注意:これにより、get()の規約が破られます。マップにキーが含まれていない場合は、nullが返されます。適合するためには 'containsKey()'をオーバーライドして*常に* trueを返してください。私は 'makeComputingMap()'もそれを行うと思いますが、明示的にドキュメント内に記述されていないと思います。 –

+3

@マーク:おそらく。しかし、キーを反復することで、トリッキーになりますが、基本的には遅かれ早かれ抽象化が中断します。 *どこで*あなたはそれが間違いなく深い配慮のために問題になるでしょう:) –

+1

'/ etc 'は小さくないので、この解決策は実用的ではありません。 –

0

をあなたが単にあなたが望む値を取得するには、ラッパー関数を記述し、それがnullだ場合は代わりにデフォルト値を返すことができるように私には思えます。

Map<String, List<String>> myMap = new Map<String, List<String>>(); 

public List<String> myGet(String key) { 
    List<String> ret = myMap.get(key); 
    if(ret == NULL) { 
     return defaultList(); // where defaultList is a function that generates your default list. 
    } 
    return ret; 
} 

これは、それはあなたが、あなたはまた、MyMapのようなものにMapまたはHashMapを拡張することができ、ちょうどこの余分な機能を備えて欲しいものではありません場合は、あなたのmyMapは、(簡単のために)グローバル変数であることを前提としています。そうすれば、MyMapオブジェクトがインスタンス化されている場所からでも使用できます。

+0

ええと、上に挙げたデコレータのパターンは、もっと良いアイデアのようです... – Argote

+2

マップが必要なものにマップを渡す必要がある場合、これはあまり役に立ちません。 –

8

動作を変更する場合は、getメソッドをオーバーライドするだけで、以前の投稿と同様です。

Map<String, List<String>> map = new LinkedHashMap<String, List<String>>() { 
    public String get(Object key) { 
     List<String> list = super.get(key); 
     if (list == null && key instanceof String) 
      super.put(key, list = new ArrayList<String>()); 
     return list; 
    } 
}; 
7

グアバには、あなたが望むものを正確に行う方法があります。これはArgote's answerに似ています。

Map<String, List<String>> myMap = ... 
Functions.forMap(myMap, Arrays.asList("default", "value")); 
+0

とても素敵なワンライナーです。 :D – epicrose

+2

おそらく私は何か不足していますが、これはデフォルト値を持つ 'Map'実装をどうやって作りますか? – ach

+1

それはしません。 Guavaの 'Function'を作成します。これは' Map'に似ています。 'Map'インタフェースはデフォルト値を許可しません。 –

25

default方法のおかげで、Javaの8は今、これはMap::getOrDefaultで構築されています

Map<Integer, String> map = ... 
map.put(1, "1"); 
System.out.println(map.getOrDefault(1, "2")); // "1" 
System.out.println(map.getOrDefault(2, "2")); // "2" 
+2

これはグアバを必要としないので、これは最もスリムな答えです。そして、それは私が探していたものでした。アップボーニング。 –

+2

これはタイミングに基づいているように見えますが、これは最善の解決策ですが、遅れてパーティーに来ました(感謝のJava 8!)。私はそのシンプルさが好きです。 +1 –

関連する問題