[英]Java generics - Map of (typed) maps
我正在研究一種(簡單的)緩存解決方案,其中服務可以從緩存映射中請求緩存對象。 Cache對象在本質上也與Map一樣,它具有鍵和值以及訪問和存儲對象的方法。
我想出了以下解決方案,但是正如您所看到的,它包含一個強制轉換(因為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");
}
現在,我的問題是:
編輯:哦,很多答案,謝謝! 在此處進行編輯以描述我在實際測試時發現的奇怪之處:
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.
您可以創建一個包含當前標識符和兩個Class實例的組合鍵(一個用於鍵,一個用於值)
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);
}
}
為了使此線程安全,請改用ConcurrentHashMap和putIfAbsent(..)
關於線程安全性問題,不,它不是線程安全的。 您應該查看ConcurrentHashMap或Google Guava的MapMaker
您可以使整個方法同步化以使其線程安全。 如果不經常調用它將足夠有效。 如果要使代碼更安全,建議您嘗試以下操作,為類型添加運行時檢查。
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;
}
只要正確處理classcastexceptions,這是“安全”的方法嗎? (可能會捕獲它們並將它們打包到自定義異常類中)
實際上,最安全的處理方法是使用鍵和值類型創建緩存鍵。 像這樣:
public String getCacheKey(Class<?> keyType, Class<?> valueType, String uniqueId){
return keyType.getName()+"-"+valueType.getName()+"-"+uniqueId;
}
這樣,您將確保高速緩存屬於指定類型。
有“安全”的選擇嗎? 如果可能的話,可以使用泛型,因為我喜歡它們並且不喜歡演員表。
基本上:如果您不喜歡未經檢查的轉換,則必須為所有方法提供實現類型。
(不直接相關)此線程安全嗎? 我認為不是,但是,我不是線程專家。 僅使整個方法同步就足夠了嗎,還是(有六個客戶端)會導致過多的開銷/鎖定? 有沒有一個整潔的解決方案?
同步方法很糟糕。 使用ConcurrentHashMap
及其putIfAbsent()
方法
昨天也有類似的問題 。 您的解決方案並不安全,因為關於鍵的任何事情都沒有暗示值的類型。 在另一個問題中,鍵是Callable<T>
,值是T
因此,可以創建一個自定義映射,以確保類型安全,並防止基礎映射被破壞。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.