简体   繁体   English

Android replaceAll和==的字符串比较

[英]Android replaceAll and string comparison with ==

While auditing an android source code, I found a string comparison bug which used == instead of equals(). 在审核android源代码时,我发现了一个使用==而不是equals()的字符串比较错误。 However, the app is working well surprisingly! 但是,该应用程序运行异常良好!

After some testing, I found that replaceAll() method is hiding the bug. 经过一些测试,我发现replaceAll()方法隐藏了该错误。

String description = " ";
description = description.trim();
Result1.setText(description + " == " + "" + ": " + (description == ""));

prints "==:false" as I expected. 如我所料,打印“ ==:false”。 However, 然而,

String description = " ";
description = description.trim().replaceAll("\\s+|\\r+|\\n+", " ");
Result1.setText(description + " == " + "" + ": " + (description == ""));

prints "==:true"! 打印“ ==:true”! (Android 4.4.2, API 19) (Android 4.4.2,API 19)

I run the same code in my desktop (javac 1.6.0_45) and it prints "==:false" as I expected. 我在桌面(javac 1.6.0_45)中运行了相同的代码,并且按预期显示了“ ==:false”。

Is it a bug in Android or is it intended behavior? 这是Android中的错误还是预期的行为?

No, it's not a bug -- it's just an implementation detail leaking out. 不,这不是错误-只是泄漏了实现细节。

The java compiler creates a pool of strings used in the code. Java编译器创建代码中使用的字符串池。 The empty string is surely one of these. 空字符串肯定是其中之一。 Any time you set a variable to the empty string at compile-time, it will point to the same instance of the empty string. 每次在编译时将变量设置为空字符串时,它将指向空字符串的相同实例。 So 所以

String a = "";
String b = "";

if (a == b) {
   //this will always be true in practice, although I don't know if it's guaranteed
}

Now imagine that trim() and replaceAll() are implemented differently: 现在,假设trim()和replaceAll()的实现方式有所不同:

String trim() {
    byte[] b = getBytes();
    ...
    return new String(b, 0, len);
}

String replaceAll (String needle, String replacement) {
    String result = "";
    int pos = 0;
    while (indexOf(needle, pos) != -1) {
        ...
        result = result + replacement;
        pos = ...;
    }
    return result;
}

Because trim() calls a String constructor, it necessarily creates a new String. 因为trim()调用String构造函数,所以它必然创建一个新的String。 But replaceAll starts with an empty String and builds up. 但是replaceAll以空的String开头并建立。 And the empty String that it starts with is the same empty string as all the other empty strings in your source code. 它开头的空字符串与源代码中的所有其他空字符串相同。

These are fake implementations -- it's just a hypothesis that this is how it works. 这些是伪造的实现-只是假设它是这样工作的。 It matches the observed data, but I didn't read the Android code. 它与观察到的数据匹配,但是我没有阅读Android代码。 Still, it demonstrates that different implementations of similar functions could lead to the effect that you're seeing. 尽管如此,它仍然表明相似功能的不同实现可能会导致您所看到的效果。

In other words, it's not a bug, but it's not a behavior you want to depend on. 换句话说,这不是错误,但不是您要依赖的行为。 If you do want to depend on two strings that .equal() also being ==, you can use String.intern(). 如果确实要依赖.equal()也==的两个字符串,则可以使用String.intern()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM