2010-03-17 120 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