2010-03-17 113 views
4

我一直在尝试3个小时,我无法理解这里发生了什么。在Java枚举中递归?

我有一个枚举“迷宫”。出于某种原因,当在这个枚举上调用搜索方法时,它非常慢(运行3分钟)。但是,如果我将相同的方法作为静态方法复制到另一个类中,并且我从枚举'迷宫'中调用它,它将在一秒钟内运行!

我不明白为什么? Java枚举中的递归方法有什么问题吗?我究竟做错了什么?

public enum Maze 
{ 
    A("A.txt"), B("B.txt"); 

    // variables here... 

    Maze(String fileName) 
    { 
     loadMap(fileName); 
     nodeDistances = new int[nodes.size()][nodes.size()]; 
     setNeighbors(); 
     setDistances(); 
    } 

    ... more methods here ... 

    private void setDistances() 
    { 
     nodeDistances = new int[nodes.size()][nodes.size()]; 

     for (int i = 0; i < nodes.size(); i++) { 
      setMax(nodeDistances[i]); 
      // This works!!! 
      TestMaze.search(nodes, nodeDistances[i], i, 0); 
      // This DOESN'T WORK 
      //search(nodes, nodeDistances[i], i, 0); 
     } 
    } 

    public void setMax(int[] a) { 
     for (int i=0; i<a.length; i++) { 
      a[i] = Integer.MAX_VALUE; 
     } 
    } 

    public void search(List<Node> allNodes, int[] distances, int curNodeIndex, int curDist) 
    { 
     if (curDist < distances[curNodeIndex]) 
     { 
      distances[curNodeIndex] = curDist; 

      for (Node n : allNodes.get(curNodeIndex).getNeighbors()) { 
       search(allNodes, distances, n.getNodeIndex(), curDist + 1); 
      } 
     } 
    } 
} 

public class TestMaze 
{ 
    public static void search(List<Node> allNodes, int[] distances, int curNodeIndex, int curDist) 
    { 
     if (curDist < distances[curNodeIndex]) 
     { 
      distances[curNodeIndex] = curDist; 

      for (Node n : allNodes.get(curNodeIndex).getNeighbors()) { 
       search(allNodes, distances, n.getNodeIndex(), curDist + 1); 
      } 
     } 
    } 
} 
+0

尝试添加一些disgnostic输出看到获得通过的递归调用什么参数,并且其中大部分时间都花在... – 2010-03-18 00:00:43

+0

是问题仍然存在,即使我让静态的:S * – 2010-03-18 00:10:27

+2

*为什么你使用枚举而不是类? – 2010-03-18 00:20:45

回答

5

这种奇怪的行为似乎与JIT有关。这似乎是方法的工作速度较慢,如果他们被自己的班级的初始化期间JIT编译(即如果他们frequenty从static初始化调用,它也发生在OP的情况下,由于枚举值初始化过程中实例化)。

这里是一个小测试:

public class Test { 
    public static final long N = 40; 

    static { 
     long t = System.currentTimeMillis(); 
     computeStatic1(N); 
     System.out.println("computeStatic1 in initializer: " + (System.currentTimeMillis() - t)); 

     Test x = new Test(); 
     t = System.currentTimeMillis(); 
     x.compute1(N); 
     System.out.println("compute1 in initializer: " + (System.currentTimeMillis() - t)); 

     computeStatic2(10); // Not enough to launch JIT 
     x.compute2(10); // Not enough to launch JIT 
    }  

    public static void main(String[] args) throws Exception { 
     long t = System.currentTimeMillis(); 
     computeStatic1(N); 
     System.out.println("computeStatic1 in main: " + (System.currentTimeMillis() - t)); 

     Test x = new Test(); 
     t = System.currentTimeMillis(); 
     x.compute1(N); 
     System.out.println("compute1 in main: " + (System.currentTimeMillis() - t)); 

     t = System.currentTimeMillis(); 
     computeStatic2(N); 
     System.out.println("computeStatic2 in main: " + (System.currentTimeMillis() - t)); 

     t = System.currentTimeMillis(); 
     x.compute2(N); 
     System.out.println("compute2 in main: " + (System.currentTimeMillis() - t)); 
    } 

    private long compute1(long n) { 
     if (n == 0 || n == 1) return 1; 
     else return compute1(n - 2) + compute1(n - 1); 
    } 

    private static long computeStatic1(long n) { 
     if (n == 0 || n == 1) return 1; 
     else return computeStatic1(n - 2) + computeStatic1(n - 1); 
    } 

    private long compute2(long n) { 
     if (n == 0 || n == 1) return 1; 
     else return compute2(n - 2) + compute2(n - 1); 
    } 

    private static long computeStatic2(long n) { 
     if (n == 0 || n == 1) return 1; 
     else return computeStatic2(n - 2) + computeStatic2(n - 1); 
    } 
} 

结果(太阳JVM):

> java Test 
computeStatic1 in initializer: 4360 
compute1 in initializer: 4578 
computeStatic1 in main: 4359 
compute1 in main: 4610 
computeStatic2 in main: 2859 
compute2 in main: 2859

JIT禁用:

> java -Xint Test 
computeStatic1 in initializer: 20578 
compute1 in initializer: 21656 
computeStatic1 in main: 20250 
compute1 in main: 21391 
computeStatic2 in main: 20250 
compute2 in main: 21375

在IBM J9 VM这种行为仅观察到对static方法:

$ java Test 
computeStatic1 in initializer: 5053 
compute1 in initializer: 2488 
computeStatic1 in main: 5044 
compute1 in main: 2473 
computeStatic2 in main: 2597 
compute2 in main: 2489
+0

哇!有趣! – irreputable 2010-03-18 04:00:52

0

一个明显的问题是,做搜索的两个变种()都产生正确的结果?

我最好的猜测是,你正在运行到一个类加载顺序问题。这是因为您的搜索例程作为枚举的构造函数的副作用被启动。既然你没有发布完整的代码,我不能肯定地说,但试试这个:

Maze(String fileName) 
{ 
    //show when the individual enum values get constructed 
    new Exception("constructor for " + fileName).printStackTrace(); 

    loadMap(fileName); 
    nodeDistances = new int[nodes.size()][nodes.size()]; 
    setNeighbors(); 
    setDistances(); 
} 

...看看你得到不同的结果,当你调用枚举的方法search与静态版本在另一个班级。

+0

它显示完全相同的两个... – 2010-03-18 01:32:37