Java 如何决定数学表达式中的哪个运算符必须被(取消)装箱?

How does Java decide which operator in a math expression has to be (un)boxed?

我目前正在写关于如何编写有效 Java 代码的学士论文。以下四个代码片段是 JMH 基准测试的一部分,每个方法将执行 100 万次。

public final static int primitiveOnly(int dummy, int add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static int primitiveToWrapper(int dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static int wrapperToPrimitive(Integer dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static Integer wrapperToWrapper(Integer dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

这四种方法的结果是:

此行为的原因是,在 primitiveToWrapper 中的操作期间,Integer 值只需取消装箱,而在 wrapperToPrimitive 中的操作中,必须将第一个操作数装箱到 Integer 中,这会导致昂贵的对象创建。

Java 出现这种行为是否有特定原因?我通读了 The Java Language Specification 但找不到这个问题的答案。

更新:

为了解决有关 return 值的问题(感谢 Phil Anderson),我更新了我的代码。此外,我将基准 class 中的所有整数变量更改为 int。这是新版本:

public final static int primitiveOnly(int dummy, int add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int primitiveToWrapperIntDummy(int dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer primitiveToWrapperIntegerDummy(Integer dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int wrapperToPrimitiveIntDummy(int dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer wrapperToPrimitiveIntegerDummy(Integer dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int wrapperToWrapperIntDummy(int dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer wrapperToWrapperIntegerDummy(Integer dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

结果是 10 次迭代的平均值(1 次迭代 = 上述每种方法执行 100 万次)。

现在感觉最终结果更加直观。谢谢大家帮助我。

在代码的第二位中,每次为虚拟对象赋值时,java 都必须将其装入一个 Integer 中,因为这是变量的类型。它不知道您实际上从未在其上调用任何方法,并且它可能是一个简单的 int。

因此,每次它遇到代码 dummy += (add1 + add2); 时,它都必须执行以下操作。

  • 开箱假人
  • 执行加法
  • 将结果装回虚拟

每次for循环都会这样做。

这是因为当dummy是一个整数时,它的值是不可变的。参见例如Why are Integers immutable in Java?

基本上,在你写dummy += (add1 + add2);的最后一个方法中,它意味着

dummy = Integer.valueOf(dummy.intValue() + add1.intValue() + add2.intValue());

每次循环,都需要分配一个新的对象来保存一个新的整数值。