【问题标题】:Copy constructor for tree structure occasionally deletes leaves树结构的复制构造函数偶尔会删除叶子
【发布时间】:2013-01-11 01:57:00
【问题描述】:

我有一个树状结构,我的复制构造函数似乎偶尔会掉一些“叶子”。

基本结构:

public class Arrow {
   ArrayList<Arrow> subArrows;
   Interval start;
   Interval end;
}

复制构造函数:

public Arrow(Arrow other) {
    this.start = new Interval(other.start);
    this.end = new Interval(other.end);
    if (other.subArrows != null) {
        this.subArrows = new ArrayList<Arrow>();
        for (Arrow sub : other.subArrows) {
            this.subArrows.add(new Arrow(sub));
        }
    } else {
        this.subArrows = new ArrayList<Arrow>();
    }
}

我希望这实际上是对我的树结构进行深度复制。相反,我偶尔会发现我的subArrows 数组之一是空的。除了它们往往位于我的树的最低“级别”之外,我没有注意到其他模式。

有什么想法吗?好久没用java了。

编辑:有几个人要求更多代码,所以这里是所有涉及 subArrows 的地方。这是来自一个相当大的算法/数据结构,因此将其全部发布是不合理的。

递归获取所有子箭头并返回一组。

Set<Arrow> allSubArrows(Arrow arrow) {
    Set<Arrow> arrowSet = new HashSet<Arrow>();
    if (arrow.subArrows != null && arrow.subArrows.size() > 0) {
        for (Arrow sub : arrow.subArrows) {
            arrowSet.addAll(allSubArrows(sub));
        }
        return arrowSet;
    } else {
        arrowSet.add(arrow);
        return arrowSet;
    }
}

Mathy 这样做的原因,但底部修改了 subArrows:

void enforceMonotonicity(Arrow arrow) {
    boolean changed = false;
    if (arrow.end != null && arrow.start != null) {
        if (arrow.start.isParallelTo(arrow.end)) {
            //either left to right or bottom to top
            if (arrow.start.isVertical()) {
                //left interval pointing to right interval
                if (arrow.start.startGraph.y > arrow.end.startGraph.y) {
                    arrow.end.startGraph.y = arrow.start.startGraph.y;
                    changed = true;
                }
                if (arrow.end.endGraph.y < arrow.start.endGraph.y) {
                    arrow.start.endGraph.y = arrow.end.endGraph.y;
                    changed = true;
                }
            } else {
                //bottom interval pointing to top interval
                if (arrow.start.startGraph.x > arrow.end.startGraph.x) {
                    arrow.end.startGraph.x = arrow.start.startGraph.x;
                    changed = true;
                }
                if (arrow.end.endGraph.x < arrow.start.endGraph.x) {
                    arrow.start.endGraph.x = arrow.end.endGraph.x;
                    changed = true;
                }
            }
        }
    }
    if (changed) {
        //check to make sure SOMETHING is still reachable, if not arrow = null
        if (arrow.start.isVertical()) {
            if (arrow.start.startGraph.y >= arrow.start.endGraph.y ||
                    arrow.end.startGraph.y >= arrow.end.endGraph.y) {
                arrow = null;
            }
        } else {
            if (arrow.start.startGraph.x >= arrow.start.endGraph.x ||
                    arrow.end.startGraph.x >= arrow.end.endGraph.x) {
                arrow = null;
            }
        }
        //if we changed the outer arrows, we need to recursively change the subarrows
        if (arrow != null && arrow.subArrows != null && arrow.subArrows.size() > 0) {
            for (Arrow sub : arrow.subArrows) {
                enforceMonotonicity(sub);
            }
        }
    }
}

合并算法的一部分:

HashSet<Arrow> mergeCells(Set<Arrow> first, Set<Arrow> second) {
    HashSet<Arrow> mergedCell = new HashSet<Arrow>();
    //loop through arrows in adjacent cells and find the ones that connect
    for (Arrow topArrow : first) {
        for (Arrow bottomArrow : second) {
            if(topArrow.start.intersects(bottomArrow.end)) {
                //merge arrows
                Interval middle = topArrow.start.intersection(bottomArrow.end);
                Arrow newArrow = new Arrow();
                if (middle != null) {
                    //if they connect, we copy the two arrows, modify their connection,
                    //create a new arrow with the constituents as subarrows, and add that to the mergedcell
                    //after the mergedcell is created, we can delete the base arrows
                    Arrow topCopy = new Arrow(topArrow);
                    topCopy.start = middle;
                    Arrow bottomCopy = new Arrow(bottomArrow);
                    bottomCopy.end = middle;

                    newArrow.subArrows.add(topCopy);
                    newArrow.subArrows.add(bottomCopy);
                    newArrow.start = bottomCopy.start;
                    newArrow.end = topCopy.end;

                    //if end and middle are parallel, we need to project monotonicity
                    //monotonicity is already enforced within a single cell
                    //and enforcemonotonicity knows whether or not start and end are parallel
                    enforceMonotonicity(newArrow);
                }
                //enforceMonotonicity could null out the new arrow
                if (newArrow != null && !newArrow.isNull()) {
                    mergedCell.add(newArrow);
                }
            }
        }
    }

    //keep the originals in case they can connect later on
    //(hashset doesn't allow duplicates, so no need to worry here)
    mergedCell.addAll(first);
    mergedCell.addAll(second);

    return mergedCell;
}

【问题讨论】:

  • 听起来需要一些调试;)
  • 这应该没问题,除非您遇到线程问题。顺便说一句 - this.subArrows = new ArrayList&lt;Arrow&gt;(); 在这两种情况下都会发生 - 它可以出现在 if 之外。
  • 为什么不在if 之前实例化subArrows 一次?
  • 刚刚错过了,谢谢。但它并没有真正改变任何东西(编译器可能会处理它?)
  • new Arrow(sub) 会悄悄地失败吗? IE。也许它会引发一个异常,该异常在其他地方被捕获并丢弃。就此而言 - new Interval 也可能有这个问题(这可以解释为什么数组为空)。

标签: java tree copy copy-constructor


【解决方案1】:

(other.subArrows == null) 时,您的代码仍由new ArrayList&lt;Arrow&gt;() 创建this.subArrows。这不是真正的克隆(一个是null,另一个不是)。如果要确保 subArrow 始终为非空(便于在不检查 null 的情况下添加 subArrow),可以在字段定义中实例化它。

如果这仍然不起作用,您可能需要显示更多代码,因为我们不知道您如何在箭头中添加子箭头。另外,如果你的代码是在多线程环境下,而且bug很难重现,可能是同步问题。请注意,ArrayList 不是线程安全的。改用VectorCollections.synchronizedList,或正确同步关键块。同样,我们没有您的相关代码。

顺便说一句,声明 List&lt;Arrow&gt; subArrowsArrayList&lt;Arrow&gt; subArrows 更好。

【讨论】:

  • 这个问题 - 虽然有效 - 不可能像 OP 抱怨的那样丢弃“叶子”。
  • 我不确定。这可能取决于他如何添加subArrows。这就是为什么我建议他显示更多代码。
  • 将 null subArrows 更改为空列表实际上是我想要的(我意识到这不是一个精确的副本)。而且我根本没有穿线。 java是否决定在没有我明确告诉它的情况下线程化? (再次,已经有一段时间了)
猜你喜欢
  • 1970-01-01
  • 2013-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
相关资源
最近更新 更多