java编译器有多聪明?关于枚举方法的问题

keo*_*eos 1 java compiler-construction enums

考虑这种情况:

STYLE1:

static enum Style1{
    FIRE_BALL {
        @Override
        public boolean isCold() {
            return false;
        }
    },ICE_BALL {
        @Override
        public boolean isCold() {
            return true;
        }
    },FIRE_ARROW {
        @Override
        public boolean isCold() {
            return false;
        }
    },ICE_ARROW {
        @Override
        public boolean isCold() {
            return true;
        }
    };
    public abstract boolean isCold();
}
Run Code Online (Sandbox Code Playgroud)

蓝紫魅力:

static enum Style2{
    FIRE_BALL,ICE_BALL,FIRE_ARROW,ICE_ARROW;
    public boolean isCold(){
        //return this.toString().contains("ICE")?true:false; //sorry
        return this.toString().contains("ICE");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我只是想知道它是否冷.所以我要问:

编译器可以知道目标结果和常量折叠Style2吗?

如果没有,Style1应该显然更快,但更冗长.假设这是更复杂的情况,并且有更多的组合,例如BIG_FIRE_SLOW_BALL与isFast(),isBig(),Style1将以代码块结束.


所以我用jmh和jUnit做了一些测试:

与jmh:

@Benchmark
public boolean testStyle1() {
    return Style1.values()[ThreadLocalRandom.current().nextInt(4)].isCold();
}

@Benchmark
public boolean testStyle2() {
    return Style2.values()[ThreadLocalRandom.current().nextInt(4)].isCold();
}
Run Code Online (Sandbox Code Playgroud)

设置时:

            .warmupIterations(10)
            .measurementIterations(10)
            .threads(8)

Benchmark             Mode  Cnt   Score   Error  Units
EnumTest1.testStyle1  avgt   10  34.057 ± 0.101  ns/op
EnumTest1.testStyle2  avgt   10  36.196 ± 0.453  ns/op
Run Code Online (Sandbox Code Playgroud)

好吧,将线程数设置为1

            .threads(1)
Benchmark             Mode  Cnt   Score    Error  Units
EnumTest1.testStyle1  avgt   10  34.306 ± 11.692  ns/op
EnumTest1.testStyle2  avgt   10  44.279 ± 11.103  ns/op
Run Code Online (Sandbox Code Playgroud)

因此,看起来Style2无法通过编译器进行优化.

2,用jUnit:

private static final int LOOP_TIMES = 100000000;
private static final Random random1=new Random(47);
private static final Random random2=new Random(47);

@Test
public void testStyle1() {
    int cnt = 0;
    for (int i = 0; i < LOOP_TIMES; i++) {
        if(Style1.values()[random1.nextInt(4)].isCold()){
            cnt++;
        }
    }
    System.out.println(cnt);
}

@Test
public void testStyle2() {
    int cnt = 0;
    for (int i = 0; i < LOOP_TIMES; i++) {
        if(Style2.values()[random2.nextInt(4)].isCold()){
            cnt++;
        }
    }
    System.out.println(cnt);
}
Run Code Online (Sandbox Code Playgroud)

结果:

Time:      1       2       3    inverse order   4      5       6
Style1: 3.631s  4.578s  3.754s    Style2     4.131s  5.487s  4.261s  
Style2: 2.559s  4.216s  3.155s    Style1     2.316s  3.977s  4.152s
Run Code Online (Sandbox Code Playgroud)

因此,Style1可能会更快.


但为什么两个结果很接近,特别是当我同时用jmh进行测试时?或者我们应该如何处理这个?

也许给Style1一些字段来存储每个结果可能会减少冗余.但我仍觉得不太满意.希望你们中的一些人能告诉我更多.


非常感谢你们.@Andy举了一个很好的例子,我在这里添加:

enum Style4{
    FIRE_BALL,
    ICE_BALL,
    FIRE_ARROW,
    ICE_ARROW;

    private boolean cold;

    private Style4(){
        this.cold = this.toString().contains("ICE");
    }

    public boolean isCold(){
        return cold;
    }
}
Run Code Online (Sandbox Code Playgroud)

这第四种风格无需提及真或假.

Dev*_*Dev 5

如果使用构造函数,可以在某种程度上改进style1的详细程度.这将是快速的,并且(在我看来)更容易阅读.

enum Style1{
    FIRE_BALL(false),
    ICE_BALL(true),
    FIRE_ARROW(false),
    ICE_ARROW(true);

    private final cold;

    private Style1(boolean cold){
        this.cold = cold;
    }

    public boolean isCold(){
        return cold;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这三种样式中的任何一种都不可能成为代码中的热点.编写更易于阅读的代码并在以后根据需要调整性能更为重要.