如何避免java.lang.StackOverflower错误?

如何避免java.lang.StackOverflower错误?,java,recursion,stack-overflow,flood-fill,Java,Recursion,Stack Overflow,Flood Fill,我在我的绘画应用程序中实现了泛洪填充算法。 我的代码在该算法上没有问题 当我测试程序时,我注意到洪水填充对小封闭区域效果良好,但当洪水填充应用于大区域时,我得到了java.lang.StackOverflowerError,并且在重新绘制后,大区域被填充了一半。 我知道Java对递归方法的调用堆栈有限,我不确定如何优化我的代码来解决这个问题,是否需要调整BuffereImage的大小 代码: 解决方案: private class StackItem { private

我在我的绘画应用程序中实现了泛洪填充算法。 我的代码在该算法上没有问题

当我测试程序时,我注意到洪水填充对小封闭区域效果良好,但当洪水填充应用于大区域时,我得到了java.lang.StackOverflowerError,并且在重新绘制后,大区域被填充了一半。 我知道Java对递归方法的调用堆栈有限,我不确定如何优化我的代码来解决这个问题,是否需要调整BuffereImage的大小

代码:

解决方案:

    private class StackItem {
        private final int x;
        private final int y;
        private final Color previous;

        public StackItem(int x, int y, Color previous) {
            this.x = x;
            this.y = y;
            this.previous = previous;
        }
    }

    private void floodFill(final int initialX, final int initialY, final Color target, final Color previous) {
        Stack<StackItem> stack = new Stack<>();
        stack.push(new StackItem(initialX, initialY, previous));

        while (!stack.isEmpty()) {
            StackItem stackItem = stack.pop();
            if (stackItem.x > canvas.getWidth() || stackItem.x < 1 || stackItem.y > canvas.getHeight() || stackItem.y < 1)
                continue;

            if (canvas.getRGB(stackItem.x, stackItem.y) != stackItem.previous.getRGB())
                continue;

            Color previousColor = new Color(canvas.getRGB(stackItem.x, stackItem.y));
            canvas.setRGB(stackItem.x, stackItem.y, target.getRGB());

            stack.push(new StackItem(stackItem.x + 1, stackItem.y, previousColor));
            stack.push(new StackItem(stackItem.x, stackItem.y + 1, previousColor));
            stack.push(new StackItem(stackItem.x - 1, stackItem.y, previousColor));
            stack.push(new StackItem(stackItem.x, stackItem.y - 1, previousColor));

        }


    }
请原谅使用continue。我想保持原始解决方案的结构与此类似。不过,我建议不要使用它

正如您所看到的,这是一种关于如何将递归转换为循环的直接方法。我们使用的不是大小有限的JVM堆栈,而是使用使用JVM堆的集合


类StackItem只是递归函数的所有参数的表示形式。参数目标不会更改,因此它不是参数目标的一部分。每个递归调用都等于将新参数推送到堆栈结构中。递归函数的每次调用都相当于从顶部弹出参数并使用此参数执行逻辑。

最简单的解决方案是仔细检查堆栈跟踪并检测行号的重复模式。这些行号表示递归调用的代码。一旦检测到这些行,就必须仔细检查代码,并理解递归从未终止的原因。

是的,通过使用堆栈集合结构替换递归调用,可以直接解决这一问题。让我准备一个解决方案,并发布一个简短的答案。请注意,我还将函数的参数设置为final。在您最初的解决方案中,您对其中一个进行了变异—上一个。尽管这些突变是潜在bug的来源,但最好不要使用它们。
    private class StackItem {
        private final int x;
        private final int y;
        private final Color previous;

        public StackItem(int x, int y, Color previous) {
            this.x = x;
            this.y = y;
            this.previous = previous;
        }
    }

    private void floodFill(final int initialX, final int initialY, final Color target, final Color previous) {
        Stack<StackItem> stack = new Stack<>();
        stack.push(new StackItem(initialX, initialY, previous));

        while (!stack.isEmpty()) {
            StackItem stackItem = stack.pop();
            if (stackItem.x > canvas.getWidth() || stackItem.x < 1 || stackItem.y > canvas.getHeight() || stackItem.y < 1)
                continue;

            if (canvas.getRGB(stackItem.x, stackItem.y) != stackItem.previous.getRGB())
                continue;

            Color previousColor = new Color(canvas.getRGB(stackItem.x, stackItem.y));
            canvas.setRGB(stackItem.x, stackItem.y, target.getRGB());

            stack.push(new StackItem(stackItem.x + 1, stackItem.y, previousColor));
            stack.push(new StackItem(stackItem.x, stackItem.y + 1, previousColor));
            stack.push(new StackItem(stackItem.x - 1, stackItem.y, previousColor));
            stack.push(new StackItem(stackItem.x, stackItem.y - 1, previousColor));

        }


    }