Java equals primitive vs object speed

六月ゝ 毕业季﹏ 提交于 2019-12-12 06:49:56

问题


Just tried to test speed of equals when using Objects.equals vs Primitive comparison. If somebody needs the code:

import org.junit.Test;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

class BaseEquals {
    byte bytePrim;
    short shortPrim;
    int intPrim;
    long longPrim;
    float floatPrim;
    double doublePrim;
    boolean booleanPrim;
    char charPrim;

    BaseEquals() {
        bytePrim = 1;
        shortPrim = 1;
        intPrim = 1;
        longPrim = 1;
        floatPrim = 1.0f;
        doublePrim = 1.0d;
        booleanPrim = true;
        charPrim = '1';
    }
}

class EqualsObjects extends BaseEquals {

    @Override
    public int hashCode() {
        return Objects.hash(bytePrim,
                shortPrim,
                intPrim,
                longPrim,
                floatPrim,
                doublePrim,
                booleanPrim,
                charPrim);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof EqualsObjects)) {
            return false;
        }
        EqualsObjects eo = (EqualsObjects)obj;
        return Objects.equals(bytePrim, eo.bytePrim)
                && Objects.equals(shortPrim, eo.shortPrim)
                && Objects.equals(intPrim, eo.intPrim)
                && Objects.equals(longPrim, eo.longPrim)
                && Objects.equals(floatPrim, eo.floatPrim)
                && Objects.equals(doublePrim, eo.doublePrim)
                && Objects.equals(booleanPrim, eo.booleanPrim)
                && Objects.equals(charPrim, eo.charPrim);
    }
}

class EqualsPrimitives extends BaseEquals {

    @Override
    public int hashCode() {
        return Objects.hash(bytePrim,
                shortPrim,
                intPrim,
                longPrim,
                floatPrim,
                doublePrim,
                booleanPrim,
                charPrim);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof EqualsPrimitives)) {
            return false;
        }
        EqualsPrimitives eo = (EqualsPrimitives)obj;
        return bytePrim == eo.bytePrim
                && shortPrim == eo.shortPrim
                && intPrim == eo.intPrim
                && longPrim == eo.longPrim
                && Float.compare(floatPrim, eo.floatPrim) == 0
                && Double.compare(doublePrim, eo.doublePrim) == 0
                && booleanPrim == eo.booleanPrim
                && charPrim == eo.charPrim;
    }
}

public class EqualsTests {

    @State(Scope.Benchmark)
    public static class MyState {
        EqualsObjects eo1;
        EqualsObjects eo2;
        EqualsPrimitives ep1;
        EqualsPrimitives ep2;

        @Setup
        public void setup() throws Throwable {
            eo1 = new EqualsObjects();
            eo2 = new EqualsObjects();
            ep1 = new EqualsPrimitives();
            ep2 = new EqualsPrimitives();
        }
    }

    @Benchmark
    public void equalsObject(MyState state) throws Throwable {
        boolean b1 = state.eo1.equals(state.eo2);
        boolean b2 = state.eo2.equals(state.eo1);
    }

    @Benchmark
    public void equalsPrimitive(MyState state) throws Throwable {
        boolean b1 = state.ep1.equals(state.ep2);
        boolean b2 = state.ep2.equals(state.ep1);
    }

    @Test
    public void launch() throws RunnerException {
        Options options = new OptionsBuilder()
                .include(this.getClass().getName() + ".*")
                .mode(Mode.AverageTime)
                .timeUnit(TimeUnit.MICROSECONDS)
                .warmupTime(TimeValue.seconds(1))
                .warmupIterations(5)
                .measurementTime(TimeValue.seconds(5))
                .measurementIterations(10)
                .threads(2)
                .forks(1)
                .shouldFailOnError(true)
                .shouldDoGC(true)
                .build();
        new Runner(options).run();
    }
}

What I saw in the end is this results:

Benchmark                    Mode  Cnt  Score    Error  Units
EqualsTests.equalsObject     avgt   10  0.026 ±  0.001  us/op
EqualsTests.equalsPrimitive  avgt   10  0.011 ±  0.001  us/op

Do you think it is worth using primitive comparison to have faster equals methods (probably neglectable to other operations in code), or using Objects.equals to have unified code (not to think about using Double.compare and Float.compare for double and float primitives respectively, and == for other primitives) in equals method?


回答1:


Difference between both codes can be seen within their bytecode outputs.

Primitive value comparison is simply done with a single if_icmpne instruction, and thats it.

See, Instructions for bytePrim == eo.bytePrim

20: astore_2
21: aload_0
22: getfield      #3                  // Field bytePrim:B
25: aload_2
26: getfield      #3                  // Field bytePrim:B
29: if_icmpne     246                 

On the otherhand, Object comparision (Object.equals) requires primitives to be boxed to their Object equivalents (i.e. int to Integer, byte to Byte, char to Character etc.) before the comparison happens. Once both primitives are boxed, additional invokestatic instruction (Objects.equals) is invoked for completing the comparison (which internally does the primitive comparison with null checking etc.)

Instructions for Objects.equals(bytePrim, eo.bytePrim)

21: aload_0
22: getfield      #3                  // Field bytePrim:B
25: invokestatic  #4                  // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
28: aload_2
29: getfield      #3                  // Field bytePrim:B
32: invokestatic  #4                  // Method java/lang/Byte.valueOf:(B)Ljava/lang/Byte;
35: invokestatic  #30                 // Method java/util/Objects.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
38: ifeq  



回答2:


Using wrappers instead of primitives should always have a reason. Basically, you should use primitive, but sometimes you need a wrapper.

The difference is that wrappers can be null and primitive is always set to it's initial value. This means, that when you want to have your own initial states or you want to know if the received int was 0 or wasn't present you'll definitely use wrapper.

There is no surprise that comparing primitives is faster than comparing wrappers. Calling equals costs as calling any other method costs. Anyway, your test should also compare what is the difference when comparing huge numbers. Now, we can only say, that comparing primitive ones is faster than comparing wrapped ones.

Look Integer caches numbers from -128 to 127. This changes a lot.




回答3:


You must use the equals method, because '==' checks value equality between primitive types or equal object identity (i.e. whether the operands are the same instance, not just logically equal).

This all evaluates to true:

42 == 42 // primitive values
int i = 42, j = 42; i == j // primitive values
Integer i = new Integer(42); i == 42 // auto-unboxing
Integer i = 42, j = 42; i == j // cached interned Integer instance

Yet this evaluates to false, contrary to what you might expect:

Integer i = new Integer(42); Integer j = new Integer(42); i == j // not cached, different objects
Integer i = new Integer("42"); Integer j = new Integer("42"); i == j

Only use == if you're comparing primitive types or wish to actually check reference equality to see if both operands are the same instance. Even for a primitive type as one operand and a wrapper type as the other it's best not to, because the auto-unboxing can lead to nullpointer exceptions if the wrapper variable is null. It can be argued that == is also okay to use for enum constants, but that tends to lead to... debates.



来源:https://stackoverflow.com/questions/43612938/java-equals-primitive-vs-object-speed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!