在Java中使用多层HashMap的最佳实践

6
我们面临的情况是,我们最终使用了多层哈希映射;也就是说,在一个哈希映射内部还有一个哈希映射,深达三到四层。
本能地感觉这种做法不太对。我在这里读过一些关于如何迭代/使用多级哈希映射的帖子,但几乎没有一个提到了这样做的最佳实践。
为什么多级哈希映射不好,如果有更好的设计,那会是什么?
以下是我们所拥有的多级哈希映射的示例设计:
Map<String, Object1> map1;

class Object1 {
    String version;
    Map<String,Object2> map2;
}

class Object2 {
    Map<String,List<Object3>> map4;
    Map<String,String> map5;
}

你为什么认为这是不好的?只要它正确地映射到你的业务逻辑,就可以了。 - Tagir Valeev
我没有反对它的意见! :) 我听到的都是它看起来很糟糕,并且随着哈希映射内的哈希映射层级增长变得更加难以管理。我正在尝试获取一个合适的抽象模板,以处理这种类型的数据结构。 - anzaan
3个回答

4
只要它们被适当地抽象化,这并不是那么大的问题,但在可读性方面会导致一些不好的后果。如果没有抽象化,维护起来就成了开发者之间互相“噩梦”般难以承受的负担。实际上,你正在创建一个类似于表格的东西;第一个键是主键,用于访问进一步的列。对于一个简单的一、二、三级设计,这并不可怕;你需要三个键才能获取一个值。只要有方便的访问方式,如下所示,这不是个可怕的想法(虽然还有更好的方案)。
public interface Table<K1, K2, K3, V> {
    V get(K1 key1, K2 key2, K3 key3);
}

然而,这完全取决于您使用数据结构做什么。如果您发现自己正在尝试迭代中间键以获取值(也就是说,您正在查看Key 3以获取其和Key 5之间所有值的集合),那么您必须在那一点上重新考虑您的业务逻辑。提供的数据结构不足以处理所有情况;或多或少,它用于基于一组值的简单索引。或者,可以研究Guava Table,因为它执行相同类型的操作,但具有更好的接口(类似于我上面的接口)。

2
我研究了Guava表实现HashBasedTable的代码,它再次使用像Map<R, Map<C, V>>这样的数据结构。但是确实提供了更好的接口。我希望数据表示上有一些巨大的改变。 你能给一个示例说明上述设计的正确抽象吗? - anzaan

2

我认为在HashMap内部再使用HashMap是不良的做法,因为为了扩展您的HashMap以深入更多层次级别,需要花费更多的时间和金钱。从3级深度的Map5级深度的Map,您基本上需要重新编写您的类。这会在维护该程序时产生很多技术债务。

在某个地方声明初始映射

Map<String, MyHashedObject> HashKVP = new HashMap<String, MyHashedObject>();

然后创建一个 Object 用于存储额外的映射。
class MyHashedObject {

    private Map<String, MyHashedObject> InternalKvp;

    public MyHashedObject() {
        this.InternalKvp = new HashMap<String, MyHashedObject>();
    }

    /*
    * Get the next level of our MyHashedObject object
    * @param HashKey
    * @return MyHashedObject result
    */
    public MyHashedObject findHashedObject(String HashKey) {
        MyHashedObject result = null;
        if(this.InternalKvp.containsKey(HashKey)) {
            result = this.InternalKvp.get(HashKey);
        }
        return result;
    }




}

这样,您可以通过将更多的对象添加到 InternalKvp 中来轻松扩展您的 HashMap。这只是一个非常基本的例子,但您可以向 MyHashedObject 添加更多属性(例如 depthparent_object 等)。您可以像 小世界网络 一样,跟踪每个对象的 depth
这也可以通过使用 RedBlackTreeAVLTree 来更好地实现,以便更轻松地遍历 Maps

我个人认为像这样的数据结构被视为一次性的。表格有它们的用处,但是经常看到它们是不寻常的。顺便说一下,HashKVP有点在无人区徘徊。 - Makoto
是的,它处于无人区,它只是一个起始对象的示例..其中包含MyHashedObject - classicjonesynz
所以,我已经花了一点时间仔细研究了一下...你能解释一下这个解决方案和OP已经提供的有什么区别吗?在我看来它们看起来很相似。 - Makoto
区别在于,为了使OP的地图深度达到5级,他必须编写3个新对象,然后将这些对象引用到现有对象中(新的Object4Object5)。而我所要做的就是编写MyHashedObject Object4 = new MyHashedObject(),然后HashKVP.put(StringRef, Object4)。区别在于完成此操作所需的时间(以及维护现有代码和创建新代码所需的时间)。 - classicjonesynz
抱歉,我不认为有什么区别。在某种程度上(通过一些间接方式),您仍然会创建多个映射,只是使用一个对象而不是几个对象。您也没有强制执行或保证任何类型的灵活性,因此牺牲了灵活性以换取可维护性。对象添加的层非常薄。 - Makoto
这是一个基本示例,展示了一种在地图中声明地图的不同方式,代码更少(如答案中所述,可以扩展为更抽象和具体的形式)。显然,你不会简单地复制/粘贴此代码并称其为功德圆满。 - classicjonesynz

1

多级HashMap并不一定是坏的,这取决于你的算法。 坏处在于它更难管理。 考虑为HasMap值使用接口(类似于存储库),这可能会使你的设计更清晰。 另一个选项是在HashMap中使用复合键。


我真的无法理解,如果代码被正确地构建,它会有多难以管理。你能详细说明一下 - “对于HasMap值使用接口(类似存储库)”? - anzaan
尝试在具有嵌套映射的结构上编写单元测试,您将会发现它有多么困难。在这种情况下,您需要抽象化来简化逻辑。 - Konstantin Pavlov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接