在使用Objects.equals和原语比较时,只是试着测试等于的速度。如果有人需要密码:
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();
}
}
我最后看到的是这个结果:
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原语,以及对于其他原语使用== )?
发布于 2017-04-25 14:47:27
这两种代码之间的差异可以在它们的字节码输出中看到。
简单地用一个if_icmpne
指令进行原始值比较,就是这样。
参见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
另一方面,对象比较(Object.equals
)要求将原语封装为它们的对象等效(如int到Integer,字节到Byte,char到字符等等)。在进行比较之前。一旦两个原语都被装箱,就会调用额外的invokestatic
指令(Objects.equals)来完成比较(这在内部用null检查进行原语比较,等等)。
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
发布于 2017-04-25 14:11:53
使用包装而不是原语总是有原因的。基本上,您应该使用原语,但有时需要一个包装器。
区别在于包装器可以是null
,原语总是设置为它的初始值。这意味着,当您想拥有自己的初始状态或想知道接收到的int
是0还是不存在时,您肯定会使用包装器。
比较原语比比较包装器要快,这一点不足为奇。调用equals
成本与调用任何其他方法的成本相同。无论如何,您的测试也应该比较比较巨大的数字有什么不同。现在,我们只能说,比较原始的要比比较包装的快。
查看Integer
将数字缓存在-128到127之间。这变化很大。
发布于 2017-04-25 14:45:07
您必须使用equals
方法,因为'==‘检查原始类型或相等对象标识之间的值相等(即操作数是否是相同的实例,而不仅仅是逻辑上的相等)。
这全部计算为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
然而,这一结果是错误的,与您可能预期的相反:
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,自动取消装箱也会导致空指针异常。可以说,==
也可以用于枚举常量,但这会导致.辩论。
https://stackoverflow.com/questions/43612938
复制相似问题