【问题标题】:How to solve StackOverflowError with nested ArrayLists?如何使用嵌套的 ArrayLists 解决 StackOverflowError?
【发布时间】:2014-08-12 02:54:47
【问题描述】:

我在一些 Android 设备上运行我的项目,我遇到了我认为与嵌套 ArrayLists 有关的错误。

这是基本设置 - 我有一个由四肢组成的图形(将它们视为节点)。每个节点可以有更多的子节点等等。因此,每个Node 类都有一个成员ArrayList<Node> childrenNodes,其中包含任意数量的子Nodes

我无法准确指出出现问题的原因,但在彼此连续添加约 220 个节点(本质上是创建一长串连接的节点)之后,应用程序崩溃了。

E/AndroidRuntime(9299): FATAL EXCEPTION: GLThread 674
E/AndroidRuntime(9299): Process: __, PID: 9299
E/AndroidRuntime(9299): java.lang.StackOverflowError
E/AndroidRuntime(9299): at __.Node.getSelectedNode(Node.java:283)
E/AndroidRuntime(9299): at __.Node.getSelectedNode(Node.java:273)
E/AndroidRuntime(9299): at __.Node.getSelectedNode(Node.java:273)
E/AndroidRuntime(9299): at __.Node.getSelectedNode(Node.java:273)
...etc

函数Node::getSelectedNode向下遍历节点树,从主节点开始,找到被点击的节点,看起来像这样(去掉了不相关的代码):

public Node getSelectedNode(float x, float y)
{
    Node node = null;

    // First look through children (look at front-most child first, the last one).
    // ---------------------------------------------------------------------------
    for (int i = _childrenNodes.size() - 1; i >= 0; --i)
    {
        node = _childrenNodes.get(i).getSelectedNode(x, y);

        if (node != null)
            break;
    }

    // If still not found, check this node.
    // ------------------------------------
    if (node == null)
    {
        // if (this node is being clicked) {
        node = this;
    }

    return node;
}

所以我猜当我调用这个函数时,堆栈会被大量调用 getSelectedNode() 填满,具体取决于节点的后代数量,从而导致 StackOverflowError?

是这样吗?如果是这样,我真的不知道如何解决这个问题,我不应该使用 ArrayList 吗?

任何建议表示赞赏!


编辑:这是我发现的另一种导致 StackOverflowError 的场景,当我克隆其中一个图形时(本质上是遍历每个节点并克隆它们)。同样,问题似乎源于 Node 的子级、孙子级等太多...

我也 99% 确定没有循环,正如我已经测试过的那样,这个问题不会发生在节点数量较少的情况下,并且根本不会发生在桌面上(至少不会发生在桌面上)这个数量的节点,可能更多)。

public Node(Node parentNodeRef, Node cloneFrom)
{
    // This constructor is used when cloning a Node.
    this._parentNodeRef = parentNodeRef;

    this._x = cloneFrom._x;
    this._y = cloneFrom._y;

    // ...more cloning of members here, etc...
    // this below is the problematic code, if there's too many children of children of children, etc (I got up to about 220) - I get the error

    _childrenNodes = new ArrayList<Node>(cloneFrom._childrenNodes.size());
    for (int i = 0, numChildren = cloneFrom._childrenNodes.size(); i < numChildren; ++i)
        _childrenNodes.add(new Node(this, cloneFrom._childrenNodes.get(i)));
    }
}

【问题讨论】:

  • 您是否有可能有一个Nodes 的循环?也就是说,节点ab 作为其子节点之一,将c 作为其子节点之一,将a 作为其子节点之一。
  • @rgettman - 我想过,但不,绝对不是这样,我已经运行测试以确保。
  • 如果您没有循环引用,并且确实有足够的节点来填满堆栈,那么可能有更好的方法来为您的数据建模。你想做什么?
  • 您的代码从不使用xy。 (将它们传递给自身的递归调用不算数,因为递归调用也不会使用它们。)您认为这些参数会发生什么?
  • 您对没有循环引用的信心如何?我很难相信你得到了一个没有循环引用的 StackOverflow。

标签: java android memory arraylist stack-overflow


【解决方案1】:

使用代码定义的堆栈或队列以及 while 循环代替递归。

queue q = new queue()
q.push(root)
while not q.empty()
      node = q.pop()
      for each child of node
           q.push(child)
      //perform some code on current node

【讨论】:

    【解决方案2】:

    堆栈溢出的最常见原因是过深或无限递归。像 Scheme 这样实现尾调用优化的语言,允许特定类型的无限递归——尾递归——在没有堆栈溢出的情况下发生。这是可行的,因为尾递归调用不会占用额外的堆栈空间。

    http://en.wikipedia.org/wiki/Stack_overflow

    getSelectedNode 在 forloop 中调用自己,它会一次又一次地调用 itselg...

    【讨论】:

    • 很高兴看到它被这样直白地写出来,“过度深度递归”。这就是我的怀疑,但我不知道如何解决它。我用另一种场景编辑了我的主要帖子(克隆了图,从而克隆了构成图的节点 - 一个节点上的太多代节点在我克隆它时会创建这种“深度递归”)。
    猜你喜欢
    • 2013-11-15
    • 2016-02-14
    • 2016-05-03
    • 1970-01-01
    • 2019-12-05
    • 2019-08-16
    • 2019-10-28
    • 1970-01-01
    • 2015-03-04
    相关资源
    最近更新 更多