2012-03-18 9 views
3

私が開発しているアプリケーションでは、文字列を要素のコレクションに関連付けるいくつかのマップを使用します。 Map<String, List<String>>,Map<String, SortedSet<Object>>。多くの場合、特定のキーによって与えられたコレクション内の要素を追加/削除したり、マップ内に新しいエントリを作成したり作成したりする単純な関数が必要です。コレクションのマップを管理する一般的な方法

私はエフェクトのいくつかの一般的なメソッドを実装しましたが、putIntoCollection()メソッドは私にいくつかの問題を与えています。すべての警告を上昇しない私の実装では、以下の通りです:

public static <K, V, C extends Collection<V>> void putIntoCollection(
     Map<K, C> map, K key, V value, Class<? extends C> collectionClass) 
     throws InstantiationException, IllegalAccessException { 
    C collection = map.get(key); 
    if (collection == null) { 
     collection = collectionClass.newInstance(); 
     map.put(key, collection); 
    } 
    collection.add(value); 
} 

CCollectionの任意の型を指定でき、コレクションの種類を表し、Class<? extends C>パラメータは私がする具象クラストークンを渡すことができます新しいCのインスタンスを生成してください(例えば、のマップに対してArrayListのトークンを渡してください)。

しかし、私はこのようにそれを使用しようとします

Map<String, Set<String>> tags; 
String key, value; 
MapUtilities.putIntoCollection(map, key, value, HashSet.class); 

私はコンパイルエラーを取得:

The parameterized method <K, V, Set<V>>putIntoCollection(Map<K,Set<V>>, K, V, Class<? extends Set<V>>) of type MapUtilities is not applicable for the arguments (Map<K,Set<V>>, K, V, Class<HashSet>) 

私はそれを期待している間、私はClass<HashSet>引数を渡しているので、それが起こる理解をパラメータを持つSetクラス。しかし、私はClassのインスタンスをどのように(または)取得できるのか分かりません。このような一般的な方法を実行するより良い方法はありますか?

答えて

2

サードパーティのライブラリを使用する立場にいるのですか?あなたは基本的に再発明しているGuava'sMultimap - ListMultimap<String, String>SortedSetMultimap<String, Object>はあなたの2つの例です。多くの実装が提供されています。特に、あなたのケースでは、ArrayListMultimapTreeMultimapです。それだけで、明示的なファクトリオブジェクトを渡すために、一般的に簡単だ、と述べた

interface Supplier<T> { 
    T get(); 
} 

void putIntoCollection(Map<K, Set<V>>, K, V, Supplier<Set<V>> emptySetSupplier); 
+0

私はライブラリを知らなかった。私は一般的に一般的なライブラリを使うのが好きではなく、よりコンパクトなアプローチが好まれます。それにもかかわらず、私は最初にそれらのメソッドを書くのを始めたときに問題を過小評価したと思います。私はMultimapsの使用を検討したり、使いやすいようにファクトリオブジェクトを使用したりします。好奇心のために、私はまだ "純粋な" Javaでその操作を表現する方法があるかどうかを知りたいです。 –

+0

「純粋な」Javaでこれを行う良い方法は、私が記述したもの以外にはありません。つまり、あなたはこの質問に対する他の答えから、グアバがむしろソーシャルであることに気づいているかもしれないと言っています(基本的にオープンソースのGoogleのユーティリティライブラリです)。 (ディスクロージャー:私はGuavaチームにいる) –

1

...私はGuiceのは、この問題を回避するためにTypeLiteralを使用していますね。

しかし、私はあなたの質問を回避するつもりです。あなたが本当に欲しいものはGuavaのMultimapです。これはMapと似たコレクションですが、複数の値を単一のキーに関連付けることができます。また、あなたが探していたものなどのユーティリティメソッドを提供します。

あなたはグアバのwikiで詳細な説明を見つけることができます:ジェネリックでビットをいじる後http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap

1

を、これは私が見つけることができる最高の(!ワーキング)ソリューションです。

@SuppressWarnings("unchecked") 
public static <K, V, C extends Collection<V>> void putIntoCollection(
    Map<K, C> map, K key, V value, Class<?> collectionClass) 
throws InstantiationException, IllegalAccessException { 

    C collection = map.get(key); 
    if (collection == null) { 
     collection = (C) collectionClass.newInstance(); 
     map.put(key, collection); 
    } 
    collection.add(value); 

} 

unchecked警告Class<?>パラメータと(C)キャストが避けられません。このメソッドで問題が発生した場合は、型を削除するようになります。実行時にマップ内のコレクションのジェネリック型を正確に指定することはできません。この情報はコンパイル時にのみ存在し、プログラムの実行中に失われます。

さて、これは問題なく動作します:

Map<String, Set<String>> map = new HashMap<String, Set<String>>(); 
String key="x", value="y"; 
putIntoCollection(map, key, value, HashSet.class); 

は注意してください、しかし、これはまた、コンパイルエラーなしで動作すること:

putIntoCollection(map, key, value, Vector.class); 

を現在の形では、で指定する方法はありませんマップ内のコレクション値(タイプSet<String>)は、メソッド内でインスタンス化されたコレクション値と同じタイプです(第1の例ではHashSet、これは正しく、2番目の例ではVectorです。間違っています)。コンパイル時には、型の消去のために、型の削除のために、両方のコレクションインスタンスがうまく動作します(HashSetVector)。は、Stringの要素を含みますが、実行時にこの行は最初の例では機能しますが、 ClassCastExceptionと例:

Set<String> set = map.get(key); 
0

Class<List>Class<Collection>のサブクラスではありませんので、あなたのアプローチは機能しません。したがって、Classのインスタンスまたは関心タイプ(? extends C)を持つ他のコンテナをジェネリックタイプとして使用することはできません。あなたはプロトタイプのような直接のパラメータとしてCしか使用できません。

たとえば、<T> T[] java.util.List.toArray(T[] a)を見てください。このメソッドは、結果の配列のタイプTを知りたいだけです。 Class<T>として配信することはできないため、このメソッドはクライアントにインスタンスを渡す必要があります。