使用嵌套的HashMap是一种不好的做法吗?

10

让我解释一下我的情况。 我有一些需要维护的层次结构。 下面是一个显示此层次结构的图像。 图像后面我会进行解释。

   A
   |          +--> A1.1 ---> X
   |          | 
   +--> A1 ---+--> A1.2 ---> Y
   |          |
   |          .
   +--> A2    .
   .
   .
  • AA1, A2...一对多的关系
  • A1A1.1,A1.2...一对多的关系
  • A1.1X和从A1.2Y一对一的关系。

最初我设计时使用多个HashMaps来维护这个关系。但后来我很快意识到更新变得非常困难。

拥有多个HashMaps意味着我必须自己处理不同关系之间的唯一性。例如,A1.1也可以存在于根节点B中。所以我必须将A附加到A1.1中以确保唯一性。现在,如果我必须修改值A,那么我会遇到很大的麻烦,因为我已经用它来限定A中所有键的名字为A_A1.1

现在我在考虑使用嵌套的HashMaps。所以代码变成这样:

HashMap<String, HashMap<String, HashMap<String, CustomObject>>> _worldsBiggestHash; 

这种做法可以吗?我需要做很多簿记工作,因为我要使用嵌套哈希表,但是至少增删改查和唯一性问题是自动处理的。

如果这样不行,有人能建议我另一种更好的结构吗?


6
使用三层嵌套的 HashMap 已经表明设计不好。你应该考虑创建自己的类来隐藏嵌套的映射。 - Maroun
@Shrayas 昂贵是指什么意思? - Maroun
好的,既然我有所有这些地图的单独地图,我必须将A1.1标识为A_A1.1。这意味着如果我需要将A更改为NEW_A,那么我必须将所有相关的A_A1.1更改为NEW_A_A1.1,这意味着我必须遍历包含A_A1.1、A_A1.2...作为键的哈希表中的每个键。 - Shrayas
我有点害怕回答 :-) 以免被踩...但我的意思是创建一个名为Example1的新类,其中包含数据成员HashmapString等。 - Hungry Blue Dev
@ambigram_maker 即使如此,我如何保持层次结构? - Shrayas
显示剩余4条评论
2个回答

6
你显然想建模一棵树。这棵树不一定与一个明确的TreeMap相关。而且,我承认我不知道TreeMap如何帮助表示整个结构(尽管可以在单个树节点中使用它)。
你可以创建一个类,像这样:
class Node 
{
    private final String name;
    private final Map<String, Node> children;
    private final CustomObject customObject;

    Node(String name, CustomObject customObject)
    {
        this.name = name;
        this.children = new LinkedHashMap<String, Node>();
        this.customObject = customObject;
    }

    String getName()
    {
        return name;
    }

    void addChild(Node child) 
    {
        children.put(child.getName(), child);
    }

    void removeChild(String name)
    {
        children.remove(name);
    }

    Node getChild(String name)
    {
        return children.get(name);
    }

    Set<Node> getChildren()
    {
        return Collections.unmodifiableSet(
            new LinkedHashSet<Node>(children.values()));
    }

这只是一个简单的概述。

接下来,您可以按照以下方式构建层次结构:

Node root = new Node("", null);
Node a1 = new Node("A1", null);
Node a2 = new Node("A2", null);
root.addChild(a1);
root.addChild(a2);

Node a11 = new Node("A11", x);
Node a12 = new Node("A12", y);
a1.addChild(a11);
a1.addChild(a12);

这样就可以在层次结构中导航,而且维护关系也很容易。关于“唯一性”的问题我没有完全理解你的意思。无论如何,在这样的树形结构中,每个节点都通过其路径得到唯一标识。你甚至可以创建一个实用方法,如:
CustomObject c = root.find("A", "A1", "A11");

通过节点名称序列快速访问对象。


顺便提一句:正如已经指出的那样,深度嵌套的映射(或列表或集合)是有问题的。但不管怎样,您应该始终使用接口,例如在

Map<String, Map<String, CustomObject>> maps;

这在某些用例下可能是可以的,但取决于您想要建模的确切内容(尤其是当存在另一层时),这可能已经不方便了。

哇,这真是非常全面。我需要一段时间来理解它,并回复您。但看起来这可能会有所帮助。非常感谢,亲切的先生。 - Shrayas
这正是我在寻找的。谢谢@Marco13。我从中学到了很多。 - Shrayas

2

您的建议不可取。

如果您的要求是基于关键字以O(log(n))的时间获取项目,我建议采用以下任一方法来更好地解决这个问题:

  1. 使用TreeMap,或者如果需要并发性,则使用ConcurrentSkipListMap(这将是解决您的问题的首选方法)
  2. 使用TreeSet包含关键字,并使用单独的HashMap来映射关键字和值
  3. 创建一个封装映射的类(即HashMap Decorator),它将递归处理唯一键检查(再次强调第一种方法更为理想)

跳表相对于哈希表的优点是什么? - user541686
2
一个哈希表不支持所需的树形结构,而且哈希表为了实现线程安全会同步所有方法,这样非常慢。此外,ConcurrentSkipListMap可以在不抛出ConcurrentModificationException的情况下进行并发编辑。 - ethanfar
嘿,我不明白在TreeMap的情况下层次结构是如何表示的。你能否详细解释一下?谢谢。 - Shrayas
1
TreeMap是一种树形映射,因此默认具有树形结构的层次结构。 - ethanfar
好的,但我仍然不清楚层次结构A -> A1B -> B1可以在哪里维护。对不起,我对树和哈希表非常陌生。 - Shrayas
显示剩余6条评论

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