Mar*_*arc 6 java stack-overflow
我有两个不同的函数实现(例如,树的大小),一个是递归的,另一个是使用显式堆栈.
递归非常快(可能是因为它不需要在堆上分配任何东西)但可能会导致某些"罕见"输入上的堆栈溢出(在树的例子中,它将出现在任何不平衡的树上).显式版本较慢但不太可能导致堆栈溢出.
默认情况下使用递归实现是否安全,并通过执行显式实现从StackOverflowError异常中恢复?
这被认为是不好的做法吗?
这是一个代码的小例子:
interface Node {
List<? extends Node> getSons();
}
static int sizeRec (Node root) {
int result = 1;
for (Node son : root.getSons()) {
result += sizeRec(son);
}
return result;
}
static int sizeStack (Node root) {
Stack<Node> stack = new Stack<Node>();
stack.add(root);
int size = 0;
while (! stack.isEmpty()) {
Node x = stack.pop();
size ++;
for (Node son : x.getSons()) {
stack.push(son);
}
}
return size;
}
static int size (Node root) {
try {
return sizeRec(root);
} catch (StackOverflowError e) {
return sizeStack(root);
}
}
Run Code Online (Sandbox Code Playgroud)
我建议sizeRecursive你在你的方法中维护一个堆栈深度计数器,如果超过指定的级别,则切换到该sizeStackUsingHeap方法.不要依赖于StackOverflow例外 - 这是不好的做法.您不应该使用异常来定义算法.
interface Node {
List<? extends Node> getSons();
}
// Switch to a heap stack if the stack ever hits this level.
private static final int STACKLIMIT = 1000;
private static int sizeRecursive(Node root) {
// Start the stack depth at 0.
return sizeRecursive(root, 0);
}
// Recursive implementation.
private static int sizeRecursive(Node root, int depth) {
int result = 1;
for (Node son : root.getSons()) {
if (depth < STACKLIMIT) {
result += sizeRecursive(son, depth + 1);
} else {
// Too deep - switch to heap.
result += sizeUsingHeap(son);
}
}
return result;
}
// Use this when the stack gets realy deep. It maintains the stack in the heap.
private static int sizeUsingHeap(Node root) {
Stack<Node> stack = new Stack<>();
stack.add(root);
int size = 0;
while (!stack.isEmpty()) {
// I am assuming this algorithm works.
Node x = stack.pop();
size++;
for (Node son : x.getSons()) {
stack.push(son);
}
}
return size;
}
// Always use sizeRecursive to begin with.
public static int size(Node root) {
return sizeRecursive(root);
}
Run Code Online (Sandbox Code Playgroud)
嗯,这是一个见仁见智的问题。但是,我认为你不应该这样做。首先,你的逻辑扭曲了异常处理的含义(异常是“异常”而不是逻辑),其他程序员在解释你的代码时会遇到问题。
除此之外,您不应该捕获指示环境运行时问题的“错误”。你应该问自己是否值得忘记一些好的做法。也许,您可以尝试调整运行时配置以适应应用程序或添加额外的验证逻辑...您的调用...但是考虑到安全性,您实际上不能说您很好,因为我们不知道堆栈状态如何现在是这样,不同的 JRE 实现可能会有所不同。
最后,对于底部的问题:这是一种不好的做法,而且不安全。
当然,在某些情况下,堆栈溢出可能会导致应用程序不一致,就像内存耗尽一样。想象一下,在嵌套的内部方法调用的帮助下构造并初始化某个对象 - 如果其中一个抛出异常,则该对象很可能处于一种不应该发生的状态,就像分配失败一样。但这并不意味着您的解决方案仍然是最好的解决方案
它有一个被称为错误而不是异常的理由......
来自文档:
公共抽象类VirtualMachineError扩展了错误:抛出该异常表示Java虚拟机已损坏或已耗尽继续运行所需的资源
public class Error extends Throwable:Error 是 Throwable 的子类,它指示合理的应用程序不应尝试捕获的严重问题。大多数此类错误都是异常情况。ThreadDeath 错误虽然是“正常”情况,但也是 Error 的子类,因为大多数应用程序不应尝试捕获它。方法不需要在其 throws 子句中声明在方法执行期间可能抛出但未被捕获的任何 Error 子类,因为这些错误是永远不应该发生的异常情况。也就是说,Error 及其子类被视为未经检查的异常,以便进行异常的编译时检查。
| 归档时间: |
|
| 查看次数: |
513 次 |
| 最近记录: |