【问题标题】:TreeMap with Classes as Key以类为键的 TreeMap
【发布时间】:2015-12-29 20:24:52
【问题描述】:

我正在尝试为不同类的对象编写一种注册表。

我有以下几点:

public interface DbObject{
    void setId(long id);
    Long getId();
}

实现此接口的原型类如下:

public class BasicDbObject implements DbObject{
    private long id=null;
    void setId(long id){
        this.id=id;
    }
    Long getId(){
        return id;
    }
}

我想构建这个接口的各种不同的实现。 而且我希望能够拥有一个 Map 对象,该对象从每个实现类映射到一个实例 Map。

类似这样的:

Map <Class<C implements DbObject> , Map<Long, C>> registry = new TreeMap/HashMap/SomeOtherKindOfMap (...)

我知道我可以做类似的事情

Map <String,Map<Long,DbObjects>> registry = new ...

但是这样我就不得不编写更多的代码来确定名称、比较类等等。有没有更简单的方法来做到这一点?

那么我想知道的是:是否可以将类对象作为树形图中的键?

声明映射对象的语法是什么,从实现类 C 映射到映射对象,每个映射对象从长对象(id)映射到 C 的实例?

我希望能够执行以下请求:

BasicObject bo = registry.get(BasicObject.class).get(42);

假设 id 做到了

BasicObject bo=new BasicObject(...);
innerMap = new SomeMap<Long,BasicObject>();
innerMap.put(42,bo);
registry.put(BasicObject.class,innerMap);

之前。

请告诉我,如果这仍然不清楚,我很难解释,因为英语不是我的母语。

提前谢谢你。


编辑:

事实证明,在围绕地图定义泛型类时,我可以做一些非常接近我想要的事情:

public class ObjectRegistry <T extends DbObject>{

    private HashMap<Class<T>, TreeMap<Long,T>> registry=null;

    ObjectRegistry(){
        registry=new HashMap<Class<T>, TreeMap<Long,T>>();
    }
    public void register(T dbObject){
        TreeMap<Long, T> map = registry.get(dbObject.getClass());
        if (map==null){
            map=new TreeMap<Long,T>();
            registry.put((Class<T>) dbObject.getClass(),map);
        }
        map.put(dbObject.getId(),dbObject);
    }

    public <T extends DbObject>T get(Class<T> objectClass,long id){
        TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(objectClass);
        if (map != null){
            return map.get(id);
        }
        return null;
    }

    public TreeMap<Long,T> getAll(Class<T> dbObjectClass) {
        return registry.get(dbObjectClass);
    }
}

我将 TreeMap 用于内部映射,因为我想轻松返回按 id 排序的 Class 实例。

所以精炼的问题是: 有没有办法做到这一点,没有类头中的&lt;T extends DbObject&gt; 子句?


编辑 2:

再想一想,原来约翰的回答正是解决这个问题的办法。

这是我的最终代码:

HashMap<Class<? extends DbObject>, TreeMap<Long, ? extends DbObject>> registry = null;

public <T extends DbObject> T get(Class<T> clazz, long id) {
    TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(clazz);
    if (map != null) {
        return map.get(id);
    }
    return null;
}

public <T extends DbObject> void register(T dbObject) {
    TreeMap<Long, T> map = (TreeMap<Long, T>) registry.get(dbObject.getClass());
    if (map == null) {
        map = new TreeMap<Long, T>();
        registry.put((Class<T>) dbObject.getClass(), map);
    }
    map.put(dbObject.getId(), dbObject);
}


public <T extends DbObject> TreeMap<Long, T> getAll(Class<T> dbObjectClass) {
    return (TreeMap<Long, T>) registry.get(dbObjectClass);
}

它不需要类头中的&lt;T extends DbObject&gt; 子句。

【问题讨论】:

    标签: java class generics interface treemap


    【解决方案1】:

    那么我想知道的是:是否可以将类对象作为树形图中的键?

    TreeMap 取决于键空间的总顺序,由具有自然顺序的键类型(通过实现Comparable)或您提供的单独的Comparator 对象建立。 Classes 没有自然顺序。可以想象你可以写一个合适的Comparator,但我觉得这很做作。

    但是为什么你特别需要TreeMap?您没有描述任何其他类型的Map 至少无法解决的任何要求。特别是,我几乎总是发现HashMap 是一个更好的选择,而且我看不出有什么理由不适合这个。它当然可以将Class 类型的对象作为键。

    此外,如果您确实不需要任何特定的实现,那么您最好将类型声明简单地作为Map。这样一来,您实际上可以提供任何Map 实现,甚至如果您发现这样做的理由,甚至可以更改您提供的实现。

    声明映射对象的语法是什么,从实现类 C 映射到映射对象,每个映射对象从长对象(id)映射到 C 的实例?

    您要求对每个值的类型的约束取决于关联键的类型,但没有办法声明强制这种关系的类型。特定键或特定值是否适合 Map 仅取决于映射的类型,而不是彼此的类型。

    您可以围绕访问您的地图编写通用方法,以提供您想要的外观,但数据检索方法需要强制转换。例如:

    Map<Class<? extends DbObject>, Map<Long, ? extends DbObject>> registry = /*...*/;
    
    <T extends DbObject> Map<Long, T> getRegistryMap(Class<T> clazz) {
        return (Map<Long, T>) registry.get(clazz);
    }
    
    <T extends DbObject> T get(Class<T> clazz, Long id) {
        Map<Long, T> map = getRegistryMap(clazz);
    
        return (map == null) ? null : map.get(id);
    }
    
    <T extends DbObject> T put(Class<T> clazz, Long id, T obj) {
        Map<Long, T> map = getRegistryMap(clazz);
    
        if (map == null) {
            map = new HashMap<>();
            registry.put(clazz, map);
        }
        return map.put(id, obj);
    }
    

    更新添加:

    所以精炼的问题是:有没有办法做到这一点,而类头中没有&lt;T extends DbObject&gt; 子句?

    是的,我已经写了。只需在其周围添加一个普通的类声明即可。你不需要一个泛型类来拥有泛型方法。事实上,两者是正交的。泛型类的常规 方法可以使用该类的类型参数。这并不能使它们成为通用方法。如果一个方法声明了它自己的类型参数,那么它就是通用的,就像我上面的那样。您的 get() 方法也这样做,重要的是要了解您在方法签名中显式声明的类型参数 &lt;T&gt; 会隐藏类的同名类型参数:它是不同的 T

    【讨论】:

    • 这与树形图无关,我可以使用其他类型的地图,我知道。这是关于声明地图的语法。
    • @StephanRichter,已更新答案以解决您明确的问题。
    • 好的,这基本上就是我想知道的。我会把它打开几天,也许有人有更好的想法/解决方法/无论如何。如果没有,我会在接下来的几天内将您的答案标记为正确。谢谢,到目前为止。
    • 再次编辑了问题。我非常接近解决方案。奇怪的是,这与您建议的解决方案几乎相同,现在我又读了一遍。
    • @StephanRichter,为什么这很奇怪?无论如何,我已经更新了我的答案以解决您的问题更新。我真希望这件事现在已经解决了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    相关资源
    最近更新 更多