【问题标题】:Use Java HashMap multiple Type使用 Java HashMap 多种类型
【发布时间】:2015-01-08 07:47:09
【问题描述】:

我使用 Hashmap 作为我的内存缓存。所以基本上,这就是我所拥有的:

private static final Map<String, Object> lookup = new HashMap<String, Object>();

    public static Object get(CacheHelper key) {
        return lookup.get(key.getId());
    }

    public static void store(CacheHelper key, Object value) {
        lookup.put(key.getId(), value);
    }

没关系。但是对于我从地图中“获取”的每个对象,我都必须进行投射,这非常难看。 我想把 ArrayList 和许多其他不同的东西放进去。

有人知道其他类型安全的解决方案吗?

(当然,我可以为每种类型创建一个 getter 和 setter,但这是唯一的解决方案吗?

那么,有没有更好的方法来创建内存缓存,或者有人知道如何包装 hashmap 以更安全?

【问题讨论】:

  • 你试图在这个缓存中填充多少不同类型的对象?
  • 让我们假设大约 15 种类型。这通常是正常的
  • 正如托马斯在下面提到的那样,当涉及到这些类型时,没有共同点?
  • 不,只是父对象:对象

标签: java arraylist hashmap typesafe


【解决方案1】:

解决此问题的一个方法是使您的 CacheHelper 类型为泛型,使用CacheHelper&lt;T&gt;。然后为您的地图创建一个包装器:

class MyCache {
  private final Map<CacheHelper<?>, Object> backingMap = new HashMap<>();
  public <T> void put(CacheHelper<T> key, T value) {
    backingMap.put(key, value);
  }
  @SuppressWarnings("unchecked")
  // as long as all entries are put in via put, the cast is safe
  public <T> T get(CacheHelper<T> key) {
    return (T) backingMap.get(key);
  }
}

Java 编译器实际上在内部使用了这种方法;参见例如here。您不必传递显式的 Class 对象,但您必须知道每个键实际上关联的是什么类型,这在行为良好的应用程序中应该是这样。 p>

【讨论】:

    【解决方案2】:

    您也可以存储元素的类型,将您期望的类型传递给get() 方法并在那里检查。 AFAIK 如果没有一些通用的超类或接口,没有内置的方法可以使存储不同类型的类型安全。

    基本上你会这样做:

    private static final Map<String, Object> lookup = new HashMap<>();
    private static final Map<String, Class<?>> types= new HashMap<>();   
    
    public static <T> T get(CacheHelper key, Class<T> expectedType ) {
      Class<?> type = types.get(key.getId());
      if( type == null || expectedType == null || expectedType.isAssignableFrom( type ) ) {
        throw new IllegalArgumentException("wrong type");
      }   
      return (T)lookup.get(key.getId());
    }
    
    //if null values should be allowed, you'd need to change the signature to use generics 
    //and pass the expected type as well, e.g. <T> void store(CacheHelper key, T value, Class<T> type) 
    public static void store(CacheHelper key, Object value ) {
        lookup.put(key.getId(), value);
        types.put( key.getId(), value.getClass() );
    }
    

    请注意,这仍然不会捕获任何编译时错误,因为您基本上是在缓存中禁用泛型。如果没有通用的超类/接口或缓存本身的泛型类型,就没有编译时方法来捕获这些错误 - 至少没有简单的方法。

    如果您只是不想自己进行强制转换,您可以将它们隐藏在 get() 中并忍受可能的类强制转换异常。在这种情况下,您可以让编译器从调用中推断T 的类型(通过赋值、显式设置或参数类型)。

    但是,传递预期的类很容易提供额外的信息,您可以使用这些信息来创建更好的错误消息等,而您无法从 ClassCastException 获得这些信息。

    【讨论】:

      【解决方案3】:

      您可以将Class 对象作为参数传递并使用Class.cast 方法:

      public static <T> T get(CacheHelper key, Class<T> clazz){
          return clazz.cast(lookup.get(key.getId());
      }
      

      【讨论】:

        【解决方案4】:

        如果 java 版本为 7(或更高),您可以使用 generics 功能

        public static <T> T get(CacheHelper key){
            return (T)(lookup.get(key.getId());
        }
        

        然后你可以这样称呼它:

        Myclass.<String>get(key);
        

        在上面的例子中,我假设 Myclass 是一个包含 get() 方法的类

        【讨论】:

          【解决方案5】:

          get 需要 Class&lt;T&gt;,因为类型擦除,强制转换 (T) 是一个毫无意义的空操作。

          要么一直合并类;每个班级的 ID:

          private final Map<Class<?>, Map<String, Object>> lookup = new HashMap<>();
          
          public <T> T get(Class<T> klass, String id) {
              Map<String, Object> mapById = lookup.get(klass);
              if (mapById == null) {
                  return null;
              }
              Object value = mapById.get(id);
              return klass.cast(value);
          }
          
          public <T> void store(Class<T> klass, String id, T value) {
              Map<String, Object> mapById = lookup.get(klass);
              if (mapById == null) {
                  mapById = new HashMap<>();
                  lookup.put(klass, mapById);
              }
              mapById.put(id, value);
          }
          

          store 没有使用 value.getClass() 来允许通过接口或基类获取:

          store(Number.class, "x", 3.14);
          store(Number.class, "x", 3);
          store(Number.class, "x", new BigDecimal("3.14"));
          

          或者只做

          public static <T> T get(Class<T> klass, String id) {
              Object value = lookup.get(id);
              return klass.cast(value);
          }
          

          (为了读者,我稍微简化了原始代码。)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-05-10
            • 1970-01-01
            • 2015-11-10
            • 1970-01-01
            • 2014-01-09
            • 2021-04-13
            • 2021-07-12
            • 2016-09-20
            相关资源
            最近更新 更多