首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java等于原语与对象速度

Java等于原语与对象速度
EN

Stack Overflow用户
提问于 2017-04-25 14:04:28
回答 3查看 1.5K关注 0票数 0

在使用Objects.equals和原语比较时,只是试着测试等于的速度。如果有人需要密码:

代码语言:javascript
运行
复制
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();
    }
}

我最后看到的是这个结果:

代码语言:javascript
运行
复制
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

您认为是否值得使用原语比较来拥有更快的等于方法(可能忽略了代码中的其他操作),或者使用Objects.equals拥有统一的代码(不考虑使用Double.compare和Float.compare分别用于double和float原语,以及对于其他原语使用== )?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-04-25 14:47:27

这两种代码之间的差异可以在它们的字节码输出中看到。

简单地用一个if_icmpne指令进行原始值比较,就是这样。

参见bytePrim == eo.bytePrim的说明

代码语言:javascript
运行
复制
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                 

另一方面,对象比较(Object.equals)要求将原语封装为它们的对象等效(如int到Integer,字节到Byte,char到字符等等)。在进行比较之前。一旦两个原语都被装箱,就会调用额外的invokestatic指令(Objects.equals)来完成比较(这在内部用null检查进行原语比较,等等)。

Objects.equals(bytePrim, eo.bytePrim)指令

代码语言:javascript
运行
复制
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  
票数 1
EN

Stack Overflow用户

发布于 2017-04-25 14:11:53

使用包装而不是原语总是有原因的。基本上,您应该使用原语,但有时需要一个包装器。

区别在于包装器可以是null,原语总是设置为它的初始值。这意味着,当您想拥有自己的初始状态或想知道接收到的int是0还是不存在时,您肯定会使用包装器。

比较原语比比较包装器要快,这一点不足为奇。调用equals成本与调用任何其他方法的成本相同。无论如何,您的测试也应该比较比较巨大的数字有什么不同。现在,我们只能说,比较原始的要比比较包装的快。

查看Integer将数字缓存在-128到127之间。这变化很大。

票数 0
EN

Stack Overflow用户

发布于 2017-04-25 14:45:07

您必须使用equals方法,因为'==‘检查原始类型或相等对象标识之间的值相等(即操作数是否是相同的实例,而不仅仅是逻辑上的相等)。

这全部计算为true:

代码语言:javascript
运行
复制
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

然而,这一结果是错误的,与您可能预期的相反:

代码语言:javascript
运行
复制
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

只有在比较基本类型或希望实际检查引用相等的情况下才使用==,以查看两个操作数是否是同一个实例。即使对于作为一个操作数的原始类型和作为另一个操作数的包装类型,最好不要这样做,因为如果包装变量为null,自动取消装箱也会导致空指针异常。可以说,==也可以用于枚举常量,但这会导致.辩论。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43612938

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档