【问题标题】:Java string interning, what is guaranteed?Java字符串实习,有什么保证?
【发布时间】:2013-01-06 15:00:24
【问题描述】:

问题归结为这段代码:

// setup
String str1 = "some string";
String str2 = new String(str1);
assert str1.equals(str2);
assert str1 != str2;
String str3 = str2.intern();

// question cases
boolean case1 = str1 == "some string";
boolean case2 = str1 == str3;

Java 标准是否对case1case2 的值提供任何保证? 当然,链接到 Java 规范的相关部分会很好。

是的,我查看了 SO 找到的所有“类似问题”,但没有发现重复项,因为我发现没有人以这种方式回答问题。不,这不是关于通过将equals 替换为== 来“优化”字符串比较的错误想法。

【问题讨论】:

  • 如果不是无用的优化尝试,那是什么?
  • @dystroy 我遇到的特殊情况是关于使用规范化文件名进行同步,以及用于此目的的内部字符串是否安全,或者是否需要共享 Map<String, Object> fileNameToLockObjectMap。不确定,在那种情况下我最终使用了地图(并且不会回去更改)。
  • 锁定实习字符串不是一个好主意。您很可能会产生意想不到的后果。
  • @PeterLawrey 我同意,如果不确定,请采取安全路线。但是你能想到其他这样的后果,除了在程序的完全不相关的部分中使用相同的实习 String 实例吗?如果这些字符串用于锁定别处,并且假设没有库使用这种丑陋的技巧,你能想到意想不到的后果吗?

标签: java string string-interning


【解决方案1】:

我认为 String.intern API 提供了足够的信息

字符串池,最初为空,由 String 类私下维护。

当调用 intern 方法时,如果池中已经包含一个等于该 String 对象的字符串,由 equals(Object) 方法确定,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。

由此可知,对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为真时,s.intern() == t.intern() 为真。

所有文字字符串和字符串值的常量表达式都被实习。字符串文字在 Java™ 语言规范的第 3.10.5 节中定义。

【讨论】:

    【解决方案2】:

    这是您的 JLS 报价,Section 3.10.5

    每个字符串字面量都是对实例的引用(第 4.3 节)(第 4.3.1 节, §12.5) 类 String (§4.3.3)。字符串对象有一个常量值。 字符串字面量,或者更一般地说,字符串是 常量表达式(第 15.28 节)——被“实习”以便共享唯一的 实例,使用方法 String.intern。

    因此,由编译单元(§7.3)组成的测试程序:

    package testPackage;
    class Test {
            public static void main(String[] args) {
                    String hello = "Hello", lo = "lo";
                    System.out.print((hello == "Hello") + " ");
                    System.out.print((Other.hello == hello) + " ");
                    System.out.print((other.Other.hello == hello) + " ");
                    System.out.print((hello == ("Hel"+"lo")) + " ");
                    System.out.print((hello == ("Hel"+lo)) + " ");
                    System.out.println(hello == ("Hel"+lo).intern());
            }
    }
    
    class Other { static String hello = "Hello"; }
    

    和编译单元:

    package other;
    
    public class Other { static String hello = "Hello"; }
    

    产生输出:真真真真假真

    这个例子说明了六点:

    同一包中的同一类 (§8) 中的文字字符串 (§7) 表示对同一 String 对象的引用(第 4.3.1 节)。

    同一包中不同类中的文字字符串表示 对同一个 String 对象的引用。

    不同包中不同类中的文字字符串 同样表示对同一个 String 对象的引用。

    由常量表达式(第 15.28 节)计算的字符串在 编译时,然后将它们视为文字。

    在运行时通过连接计算的字符串是新创建的,并且 因此截然不同。显式实习计算的结果 string 与任何预先存在的文字字符串相同的字符串 相同的内容。

    结合JavaDoc 实习生,你有足够的信息来推断你的两个案例都会返回true。

    【讨论】:

    • 它在多线程环境中如何工作,例如。当同时从多个线程调用 intern() 时。我想这已经解决了,但这只是一个猜测。您对此了解更多吗?
    • @Alpedar - 实习生的实现是原生的和平台相关的。我认为它是基于 JavaDoc 的线程安全的,但在支持这一点的官方文档中并不能保证(我已经找到)。
    猜你喜欢
    • 2012-05-21
    • 2011-03-27
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 2017-12-23
    • 2016-06-18
    • 1970-01-01
    • 2011-04-22
    相关资源
    最近更新 更多