我和我的一位队友进行了一次有趣的对话。
CONSTANT.equals(VARIABLE)
比VARIABLE.equals(CONSTANT)
Java快吗?
我怀疑这是一个虚假的陈述。但我试图弄清楚这背后应该是什么定性推理?
我知道在这两种情况下,性能不会因任何重要状态而有所不同。但这是最佳实践下的建议,这让我感到不舒服。这就是为什么我正在寻找一个我想用这个案例来展示的一个很好的推理。
请帮忙
我和我的一位队友进行了一次有趣的对话。
CONSTANT.equals(VARIABLE)
比VARIABLE.equals(CONSTANT)
Java快吗?
我怀疑这是一个虚假的陈述。但我试图弄清楚这背后应该是什么定性推理?
我知道在这两种情况下,性能不会因任何重要状态而有所不同。但这是最佳实践下的建议,这让我感到不舒服。这就是为什么我正在寻找一个我想用这个案例来展示的一个很好的推理。
请帮忙
有趣的问题。这是我写的测试:
public class EqualsTest {
public static String CONST = "const";
public void constEqVar(String var) {
CONST.equals(var);
}
public void varEqConst(String var) {
var.equals(CONST);
}
}
然后我使用 javac: 编译它并使用 :反javac EqualsTest.java
汇编它。javap
javap -c EqualsTest
以下是 javap 输出的相关片段:
public void constEqVar(java.lang.String);
Code:
0: getstatic #2; //Field CONST:Ljava/lang/String;
3: aload_1
4: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
7: pop
8: return
public void varEqConst(java.lang.String);
Code:
0: aload_1
1: getstatic #2; //Field CONST:Ljava/lang/String;
4: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
7: pop
8: return
正如您所看到的,这两种方法之间的唯一区别是操作顺序:在第一种情况下是 getstatic,然后是 aload_1,在第二种情况下是 aload_1 + getstatic。
很明显,执行时间不应该取决于这个顺序。
选择const.equals(var)
而不是var.equals(const)
避免的唯一原因NullPointerException
。
对我来说,这不是速度问题,而是可靠性问题。
例如
"Hello".equals(a); // will never throw a NPE
a.equals("Hello"); // can throw an NPE.
你可能更喜欢它在 a 的时候爆炸,null
但我通常不会。
这仅取决于 equals 方法的实现。它可能更快,可能更慢,而且可能是一样的……通常是一样的。此外,它不取决于一个是变量而另一个是常量这一事实,而是取决于两个对象的内容。
Constant.equals(variable) 的一个优点是您不能在 .equals 上有 NullPointerException
如果我们将CONSTANT 键(equals 方法的左侧)与任何Object(equals 方法的右侧)进行比较,那么编译器可以进行检查比较并给出预期的结果,但如果我们反之亦然Object((equals 方法的左侧))与Constant key((Right side of equals method))进行比较,那么您的程序可能会通过NULL POINTER EXCEPTION。
public static void main(String[] args) {
String CONSTANT_KEY = "JAVA";
String string = null;
// CASE 1
if (CONSTANT_KEY.equals(string)) {
System.out.println("I am in if block");
}
// CASE 2
if (string.equals(string)) {
System.out.println("I am in if block");
}
}
在上面的代码中,案例 1 总是可以安全地比较对象以避免 NULL POINTER EXCEPTION 而不是案例 2。
用字符串做了一个简单的测试:
final String constHello = "Hello";
final int times = 1000000000;
long constTimeStart = System.nanoTime();
for (int i = 0; i < times; ++i) {
constHello.equals("Hello");
}
long constTimeStop = System.nanoTime();
System.out.println("constHello.equals(\"Hello\"); " + times + " times: " + (constTimeStop - constTimeStart) + " ns");
constTimeStart = System.nanoTime();
for (int i = 0; i < times; ++i) {
"Hello".equals(constHello);
}
constTimeStop = System.nanoTime();
System.out.println("\"Hello\".equals(constHello); " + times + " times: " + (constTimeStop - constTimeStart) + " ns");
编辑: 正如下面评论中提到的,这不是进行微观测量的好方法。切换将首先执行的代码部分证明了预热时间在这里发挥了重要作用。第一个测试总是运行得较慢。在相同的代码中多次重复测试以快速修复这会使结果或多或少相同。
一个很好的比较可以是:
private static String EXAMPLE = "Example";
private String obj = null;
情况1:
if(obj.equals(EXAMPLE) {
}
这是抛出空指针异常..
案例2:
if(EXAMPLE.equals(obj)) {
}
这不会抛出空指针异常..
我想代码java.lang.String
支持我的回答:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}