2011-01-19 18 views
4

私は、キャッシュのマップからキャッシュオブジェクトを要求できる(単純な)キャッシュソリューションを開発中です。 Cacheオブジェクトは基本的にMapと同様に、オブジェクトにアクセスして格納するためのキーと値とメソッドを持っています。Java generics - (型付き)マップのマップ

私は以下の解決策を思いつきましたが、わかるように、get()はネストされたオブジェクトのタイプが何であるかを知ることができないため、キャストを含んでいます。

private final Map<String, Cache<?, ?>> caches = new HashMap<String, Cache<?, ?>>(); 

public <K, V> Cache<K, V> getOrCreateCache(String identifier) { 
    if (caches.containsKey(identifier)) { 
     return (Cache<K, V>) caches.get(identifier); 
    } else { 
     Cache<K, V> cache = new CacheImpl<K, V>(); 
     caches.put(identifier, cache); 
     return cache; 
    } 
} 

private void test() { 
    Cache<String, String> strCache = getOrCreateCache("string cache"); 
    strCache.set("key", "value"); 
} 

さて、私の質問:

  • はこれがある限りにClassCastExceptionsが適切に処理されているとして、 '安全' なアプローチですか? (おそらくそれらをキャッチし、それらをカスタム例外クラスにパックする)
  • 「安全な」代替手段はありますか?私がそれらを好き、キャストを嫌うのでジェネリックで、可能な限り、1つ。
  • (直接関係はありません)このスレッドセーフですか?私はそうではないと思うが、その後、私は糸を引く専門家ではない。全体のメソッドを同期させるだけで十分ですか、それとも半ダースのクライアントでオーバーヘッド/ロッキングが多すぎるのでしょうか?そのためのすてきな解決策はありますか?

編集:たくさんの回答、ありがとう!

Cache<String, String> someCache = service.getOrCreateCache(cacheIdentifier); 
    someCache.set("asdf", "sdfa"); 
    Cache<String, Integer> someCacheRetrievedAgain = service.getOrCreateCache(cacheIdentifier); 
    System.out.println(someCacheRetrievedAgain.get("asdf")); // prints "sdfa". No errors whatsoever. Odd. 

答えて

1

あなたが代わりにConcurrentHashMapを使用し、このスレッドを安全にするために、あなたの現在の識別子とクラスの2つのインスタンス(キーの1、値の1)

public <K, V> Cache<K, V> getOrCreateCache(String identifier, Class<K> keyClass, Class<V> valueClass) { 
    Identifier cacheIdentifier = new Identifier(identifier, keyClass, valueClass); 
    // safe cast as we know that this cacheIdentifier must has a Cache<K, V> 
    Cache<K, V> cache = (Cache<K, V>) caches.get(identifier); 
    if (cache == null) { 
    cache = new CacheImpl<K, V>(); 
    caches.put(cacheIdentifier, cache); 
    } 
    return cache; 
} 

/* 
* not the most efficient implementation, but correctly implements hashCode and equals 
* which is all we need 
*/ 
private static class CacheIdentifier extends ArrayList<Object> { 
    private CacheIdentifier(String identifier, Class<K> keyClass, Class<V> valueClass) { 
    super(3); 
    // TODO check for null 
    add(identifier); 
    add(keyClass); 
    add(valueClass); 
    } 
} 

で構成されて複合キーを作成することができますと一緒にputIfAbsent(..)

+0

私は素早く並べ替えの識別子、素敵なトリックを構成するArrayListの拡張子を覚えておくのが好きです。私はあなたの解決策を試してみます。 – fwielstra

+0

それを試した後:私はあなたのソリューションが大好きです。異なるタイプのキャッシュオブジェクトを取得しようとすると、新しいキャッシュオブジェクトが返されます。エレガント。キャッシュ識別子オブジェクトは、変更されない不変のスラッシュであるため、クライアントに永続的に格納することもできます。 – fwielstra

+0

スレッドセーフソリューションのためにcom.google.common.collect.MapMaker.makeComputingMapを使用することを検討してください。メモリや時間を節約したい場合は、ArrayListを独自のクラスに置き換えます(hashCodeとequalsの実装を忘れないでください)。 – maaartinus

0

あなたはちょうどそれがスレッドセーフにするためにsynchornized全体の方法を行うことができます。私は実際にこれをテスト中に見つかった奇妙を記述するためにここに編集。それが頻繁に呼び出されないと、十分に効率的になります。コードをより安全にしたい場合は、次のようなタイプのランタイムチェックを追加することをお勧めします。

public <K, V> Cache<K, V> getOrCreateCache(String identifier, Class<K> kClass, Class<V> vClass) { 
    Cache<K, V> cache = (Cache<K, V>) caches.get(identifier); 
    if(cache == null) 
     caches.put(identifier, cache = new CacheImpl<K, V>(kClass, vClass)); 
    assert cache.kClass() == kClass; 
    assert cache.vClass() == vClass; 
    return cache; 
} 
1

スレッドセーフティに関する質問では、スレッドセーフではありません。あなたはConcurrentHashMapを見るべきかにClassCastExceptionsが適切 を扱っているとしてGoogleグアバのMapMaker

0

これは、「安全」なアプローチである限り ? 、実際に

キーと値の型を使用してキャッシュキーを作成することですそれを処理するための最も安全な方法を(おそらくカスタム 例外クラスにそれらの をキャッチし、それらをパックする予定)。このような何か:あなたはキャッシュが指定された型であることを確認してくださいと思います

public String getCacheKey(Class<?> keyType, Class<?> valueType, String uniqueId){ 
    return keyType.getName()+"-"+valueType.getName()+"-"+uniqueId; 
} 

その方法。

「安全な」代替品はありますか?私がそれらを好きで、 キャストを嫌っているので、可能であれば、 ジェネリックで、 。

基本的に、チェックされていないキャストが嫌いなら、すべてのメソッドに実装タイプを指定する必要があります。

(これは直接関係ありません) はスレッドセーフですか?私はそうではないと思うが、その後、 私はスレッドの専門家ではない。それは だけで、全体の方法を と同期させるには十分ですか、それとも の半数のクライアントが原因であまりにも多くのオーバーヘッド/ロックが発生しますか?そこには、きちんとした 解決策がありますか?

このメソッドを同期させるのはひどいです。 ConcurrentHashMapを使用してください。putIfAbsent()メソッド

0

昨日同様のquestionがありました。値の型を意味するキーについて何もないので、あなたのソリューションは安全ではありません。他の質問では、キーはCallable<T>で、値はTでした。したがって、型の安全性を保証し、基礎となる地図が壊れないようにするカスタムマップを作成することができます。

関連する問題