簡體   English   中英

Java泛型-(類型化)映射的映射

[英]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");
}

現在,我的問題是:

  • 只要正確處理classcastexceptions,這是“安全”的方法嗎? (可能會捕獲它們並將它們打包到自定義異常類中)
  • 有“安全”的選擇嗎? 如果可能的話,可以使用泛型,因為我喜歡它們並且不喜歡演員表。
  • (不直接相關)此線程安全嗎? 我認為不是,但是,我不是線程專家。 僅使整個方法同步就足夠了嗎,還是(有六個客戶端)會導致過多的開銷/鎖定? 有沒有一個整潔的解決方案?

編輯:哦,很多答案,謝謝! 在此處進行編輯以描述我在實際測試時發現的奇怪之處:

    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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM