如何创建一个完全不可变的树层次结构?建筑鸡和蛋

2022-09-01 20:35:45

我喜欢使数据类不可变,以使并发编程更容易。但是,建立一个完全不可变的层次结构似乎有问题。

考虑这个简单的树类:

public class SOTree {
    private final Set<SOTree> children = new HashSet<>();
    private SOTree parent;

    public SOTree(SOTree parent) {
        this.parent = parent;
    }

    public SOTree(Set<SOTree> children) {
        for (SOTree next : children)
            children.add(next);
    }


    public Set<SOTree> getChildren() {
        return Collections.unmodifiableSet(children);
    }

    public SOTree getParent() {
        return parent;
    }
}

现在,如果我想创建这些节点的层次结构,那么当我构造它时,要么父节点必须在当前节点之前存在,要么子节点必须首先存在。

    SOTree root = new SOTree((SOTree)null);
    Set<SOTree> children = createChildrenSomehow(root);
    //how to add children now?  or children to the children?

    Set<SOTree> children = createChildrenSomehow(null);
    SOTree root = new SOTree(children);
    //how to set parent on children?

在不强制它是一个单链接树的情况下,是否有任何聪明的方法来构造这样的树,并且仍然具有完全不可变的所有节点?


答案 1

埃里克·利珀特(Eric Lippert)最近在博客上写了关于这个问题的博客。请参阅他的博客文章 Persistence, Facades and Roslyn's Red-Green Trees。以下是摘录:

我们实际上通过保留两个解析树来完成不可能的事情。“绿色”树是不可变的,持久的,没有父引用,是“自下而上”构建的,每个节点跟踪其宽度而不是其绝对位置。当编辑发生时,我们只重建受编辑影响的绿树部分,这通常是树中总解析节点的O(log n)。

“红色”树是围绕绿色树木建造的不可变立面;它是按需“自上而下”构建的,并在每次编辑时被丢弃。它通过在从顶部向下穿过树时按需制造父引用来计算父引用。当您下降时,它通过从宽度计算它们来制造绝对位置。


答案 2

两个想法:

  1. 使用某种树工厂。你可以使用可变结构来描述树,然后有一个工厂来组装一个不可变的树。在内部,工厂可以访问不同节点的字段,因此可以根据需要重新连接内部指针,但生成的树是不可变的。

  2. 在可变树周围构建不可变树包装器。也就是说,让树构造使用可变节点,但随后生成一个包装类,然后提供树的不可变视图。这类似于 (1),但没有显式工厂。

希望这有帮助!


推荐