Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm currently working on my Bachelor Thesis about how to write effective Java code. The following four code snippets are part of a JMH benchmark which will execute every method 1 million times each.

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;
}

The results for this four methods are:

  • primitiveOnly: 1.7 ms/operation
  • primitiveToWrapper: 2.2 ms/operation
  • wrapperToPrimitive: 47.5 ms/operation
  • wrapperToWrapper: 48.2 ms/operation

The reason for this behaviour would be that during the operation in primitiveToWrapper the Integer value has just to be unboxed where in the operation in wrapperToPrimitive has to box the first operand into an Integer which results in an expensive object creation.

Is there a specific reason Java behaves like this? I read through the The Java Language Specification but wasn't able to find a answer to this question.

UPDATE:

To address the point regarding the return values (thanks to Phil Anderson) i updated my code. In addition i changed all the Integer variables in the benchmark class to int. This is the new version:

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;
}

The results are an average of 10 iterations (1 iteration = 1 million executions of each method above).

  • primitiveOnly: 0.783
  • primitiveToWrapper (int dummy): 0.735
  • primitiveToWrapper (Integer dummy): 24.999
  • WrapperToPrimitive (int dummy): 0.709
  • WrapperToPrimitive (Integer dummy): 26.782
  • WrapperToWrapper (int dummy): 0.764
  • WrapperToWrapper (Integer dummy): 27.301

The final results now feel much more intuitive. Thanks everyone for helping me out.

like image 977
whatTheFox Avatar asked Jun 01 '15 12:06

whatTheFox


People also ask

Does Java follow Pemdas?

3 Answers. Show activity on this post. Yes, Java follows the standard arithmetic order of operations.

How does order of operations work in Java?

There are two important points to note when using Java operators: Multiplication, division, and modulo operations have precedence over addition and subtraction operations. Plus and minus operators have the same precedence. Operators with the same precedence are evaluated from left to right.

Does Java evaluate left to right?

Java expressions are evaluated following the following rules: Operands are evaluated from left to right. The operands of an operator are evaluated before the operator.


2 Answers

In the second bit of code, every time you assign a value to dummy java has to box it into an Integer because that's the type of the varaible. It doesn't know that you never actually call any methods on it and that it could be a simple int.

So each time it hits the code dummy += (add1 + add2); it has to do the following.

  • Unbox dummy
  • Perform the additions
  • Box the result back into dummy

It will do this each time through the for loop.

like image 71
Phil Anderson Avatar answered Sep 24 '22 12:09

Phil Anderson


This is because when dummy is an Integer, and its value is immutable. See e.g. Why are Integers immutable in Java?

Basically, in the last method when you write dummy += (add1 + add2);, it means

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

Every time in the loop, a new object needs to be allocated to keep a new integer value.

like image 27
dejvuth Avatar answered Sep 24 '22 12:09

dejvuth