Java - 递归 - 线程“main”java.lang.StackOverflowError中的异常

时间:2013-11-27 11:56:46

标签: java recursion

我在尝试打印出文件依赖项列表时遇到了问题。

关于该计划:

  1. 扫描给出依赖关系的* .c文件,更具体地说是查找“#include”%“
  2. 查找这些文件并以递归方式扫描它们的依赖关系
  3. 所有信息都存储在ConcurrentHashMap(key:String value:Linked List of Strings)theTable中,其中链接的字符串列表包含依赖项列表。
  4. 处理完某个文件后,我最终得到以下哈希表:

5 个答案:

答案 0 :(得分:4)

如果我理解了地图输出的内容,那么头文件中就会有一个循环(#include循环)。

i_50.h=[i_35.h, i_28.h, i_45.h, i_44.h, i_46.h],
....
i_35.h=[i_50.h, i_51.h]

这意味着您的依赖项是图形而不是DAG。而这反过来意味着简单的递归步行将无效。

从它的外观来看,你试图进行图形遍历,但由于某种原因你的循环检测/避免不起作用,你的算法进入“无限”递归。


查看代码后,我想我可以看到问题所在。在第一种方法中,检查是否已打印依赖项,然后在alreadyPrinted映射中设置条目以表明它具有。但是你继续打印无论。然后在第二种方法中,每次递归到第一种方法时,你都会(莫名其妙地)创建一个新的alreadyPrinted映射。换句话说,你的周期避免的逻辑被打破了。


我不打算为您修复代码,而是建议您转到自己喜欢的“数据结构和算法”教科书,并在索引中查找“图形遍历”。或者,这是我在一些在线讲义中找到的页面:

维基百科和其他地方的图遍历也有其他内容。 Google用于“java递归图遍历”,并尝试找到对你有用的东西。

通用算法是这样的:

    traverse(Node node):
        traverse_0(node, new Set<Node>())

    traverse_0(Node node, Set<Node> visited):
        if (visited.contains(node)) 
            return
        visited.add(node)
        for (Node child: node.children) 
            traverse_o(child, visited)

答案 1 :(得分:1)

测试依赖关系是否已经打印的唯一地方是第一个for循环。您也应该检查第二个for循环!

for (String d : dependencies) {
    if (!alreadyPrinted.containsKey(d)) {
        LinkedList<String> key = theTable.get(d);           
        if (key != null)            
            output += printDependencies(theTable, key, alreadyPrinted);
    }
}

答案 2 :(得分:1)

一旦依赖性看起来很容易看到你的算法递归:

item:  ...., item, ....

(我听到你说:“这不可能发生,因为......”然而,SO显示它确实发生了,或者你的堆栈太小了。)

顺便说一句,你保持地图“已经打印”,但它没有被使用?这暗示了您实施中的缺陷。

答案 3 :(得分:1)

当您维护某个状态(已打印和输出)时,我建议将状态移动到实例变量并使用对象而不使用类方法。

答案 4 :(得分:1)

问题是我的Graph遍历有我没有处理的循环。工作代码如下所示。

private static String printDependencies(ConcurrentHashMap<String, LinkedList<String>> theTable, LinkedList<String> dependencies, ConcurrentHashMap<String, Boolean> alreadyPrinted) {

    String output = "";

    for (String d : dependencies) {
        boolean isPrinted = alreadyPrinted.containsKey(d);
        if (!isPrinted) {
            output += " " + d;
            alreadyPrinted.put(d, true);
        }           
    }

    for (String d : dependencies) {
        LinkedList<String> key = theTable.get(d);
        if (key != null) {
            LinkedList<String> unvisited = new LinkedList<String>();
            for (String filename : key)
                if (!alreadyPrinted.containsKey(filename))
                    unvisited.add(filename);
            if (unvisited != null)            
                output += printDependencies(theTable, unvisited, alreadyPrinted);               
        }
    }

    return output;
}

private static void printDependencies(ConcurrentHashMap<String, LinkedList<String>> theTable, ConcurrentLinkedQueue<String> toProcess) {
    String output = ""; 

    for (String current : toProcess) {
        ConcurrentHashMap<String, Boolean> alreadyPrinted = new ConcurrentHashMap<String, Boolean>(); // Keeps track of dependencies already printed
        output += current + ":" + printDependencies(theTable, theTable.get(current), alreadyPrinted) + "\n";
    }

    System.out.println(output);     
}