Java枚举中的递归?

Dav*_*les 4 java recursion enums

我已经尝试了3个小时,我无法理解这里发生了什么.

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

我不明白为什么?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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

axt*_*avt 5

这种奇怪的行为似乎与JIT有关.似乎是如果它们在类的初始化期间进行JIT编译时方法工作得更慢(即,如果它们经常从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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果(Sun 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