只通过引用进行字符串相等性检查

4

我们知道,如果我们进行以下检查,输出将为equal

String s1 = "stackoverflow";
String s2 = "stackoverflow";
if(s1==s2){
    System.out.println("equal");
}

因此,我的问题是如果我在应用程序中不使用new运算符来创建String,并且所有字符串都是文字,那么我可以仅使用引用相等如上所述吗?提前致谢。
注:我正在编写一个网络爬虫,因此需要检查我是否已经访问了当前持有的给定网址。我正在使用murmur hash,它为每个URL提供一个long,但会发生哈希冲突,因此我需要检查内容是否存在哈希冲突的URL字符串。因此,为了提高性能,我考虑只比较两个字符串URL的引用相等性。我正在使用jsoup进行HTML解析。

3
如果你在编写一个递归爬虫,那么并不是所有的字符串都是字面值。不过,可以考虑使用字符串驻留技术。 - SLaks
1
如果每个字符串都是字面值,并且您不对其执行任何操作(例如子字符串、与未知变量的连接等),那么您应该可以仅使用 == - Jeroen Vannevel
请参考以下链接: https://dev59.com/DnRB5IYBdhLWcg3wyqEd - user3119954
3个回答

7
如果我在应用程序中不使用new运算符创建String,而所有字符串都是字面量,那么我可以只使用引用相等性吗? 如果您100%确定要处理的所有字符串都是普通字符串文字或编译时常量表达式,则可以。Java Language Specification §15.28规定了:

类型为String的编译时常量表达式始终被“interned”,以共享唯一实例,使用方法String.intern

但是,如果您从其他任何地方获取字符串(例如从您的爬虫检索的网页中读取它们,或者使用不是编译时常量的连接表达式构建它们),则必须使用.equals 按值比较它们而不是按引用或显式地使用.intern()

并不总是明显表达式是否为编译时常量:

String s1 = "Stack";
String s2 = s1 + "Overflow"; // not a CTC

但是
final String s1 = "Stack";
String s2 = s1 + "Overflow"; // _is_ a CTC, because s1 is a "constant variable"
                             // (final, with an initializer that is itself a CTC)

1

所以我的问题是,如果我在应用程序中不使用new运算符来创建字符串,而所有字符串都是字面值,那么我可以仅使用引用相等性,如上所述吗?

是的,当然可以。由于它们在编译时解析,所以没有问题。

但请记住以下情况:

       String s3= s2;
       String s4= s1+"";   //resolved at run time 
      System.out.println(s3==s4);  //false
      System.out.println(s3.equals(s4));//true

因此,除非您确定字符串不会在以后更改,否则可以安全地使用==。


s1+"" 这也是一个字符串字面值。请检查您的答案。 - Trying
如果s1final,则尝试该表达式仅为编译时常量(因此保证被interned)。 - Ian Roberts
@Trying 是的。没错。但是 s4 在运行时释放了该值。 - Suresh Atta
@sᴜʀᴇsʜᴀᴛᴛᴀ 请检查。 - Trying
3
尝试您的示例是完全不同的情况——两个字符串字面量的连接是编译时常量表达式,但一个字面量和一个(非常量)变量的连接则不是。 - Ian Roberts
显示剩余6条评论

1
不,你不能。虚拟机不能保证所描述的行为,这只是一种优化。要保证这种行为,你需要调用String#intern()。
只有这样才能保证引用相等。
但是进行性能测试时,String#equals()可能更快 :-)

你能提供一个详细说明为什么编译时已知的字符串不能保证被内部化的来源吗? - Jeroen Vannevel
我可能没有正确地阅读问题。我的错。其他答案更精确。 - Jan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接