5

我正在查看 String 的 openjdk 实现和私有的,每个实例成员看起来像:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];

    /** The offset is the first index of the storage that is used. */
    private final int offset;

    /** The count is the number of characters in the String. */
    private final int count;

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    [...]
}

但我知道 Java 使用字符串的引用和池,以避免重复。我天真地期待一个 pimpl 成语,其中 String 实际上只是一个 impl 的引用。到目前为止我还没有看到。有人可以解释如果我放一个 String x,Java 将如何知道使用引用吗?我的一门课的成员?

附录:这可能是错误的,但如果我处于 32 位模式,我应该计算:4 个字节用于引用“值 []”,4 个字节用于偏移量,4 个字节用于计数,4 用于哈希类 String 的所有实例? 这意味着写“String x;” 在我的一个班级中,我的班级的“权重”自动增加了至少 32 个字节(我可能在这里错了)。

4

4 回答 4

3

intern()偏移量/计数字段与池化/问题有些正交。当你有类似的东西时,偏移量和计数就会出现:

String substring = myString.substring(5);

实现此方法的一种方法是:

  • 分配一个新char[]myString.length() - 5元素
  • 将索引索引 5 中的所有元素复制到myString.length()myString 到新的char[]
  • substring是用这个新的char[]
    • substring.charAt(i)直接去chars[i]
    • substring.length()直接去chars.length

如您所见,这种方法是 O(N)——其中 N 是新字符串的长度——并且需要两次分配:新字符串和新字符 []。因此,相反,substring通过重用原始 char[] 但带有偏移量来工作:

  • substring.offset=myString.offset + newOffset
  • substring.count=myString.count - newOffset
  • 用作myString.chars字符数组substring
    • substring.charAt(i)chars[i+substring.offset]
    • substring.length()substring.count

请注意,我们不需要创建新的 char[],更重要的是,我们不需要将旧 char[] 中的 chars 复制到新的 char[] 中(因为没有新的)。所以这个操作只是 O(1) 并且只需要一个分配,即新字符串的分配。

于 2012-08-17T16:52:57.313 回答
2

Java总是使用对任何对象的引用。没有办法让它不使用引用。至于字符串池,这是由编译器为字符串文字实现的,并在运行时通过调用String.intern. 很自然,大多数实现String都忽略了它是否正在处理常量池引用的实例。

于 2012-08-17T16:23:38.773 回答
1

Java 字符串是不可变的。这意味着实现可以对内部表示做很多事情,而不会破坏任何应用程序代码。

请注意,JavaString.intern()已在 Oracle 的 JDK 实现中定义为原生的。本机代码可以访问对象的所有字段,并且可以在水下更改引用。因此,实现者所要做的就是将引用和偏移更改为字符串被实习的位置,瞧。当然,这破坏了类的不变性,因此这意味着 intern() 更新最好是线程安全的。

当您调用intern()新生成的字符串时,您可以检查这些字段会发生什么。如果什么都没发生,则可能是引用本身包含内存位置。Java 语言规范没有定义如何实现引用。

于 2012-08-17T16:40:11.023 回答
1

接受的答案和其他答案已过时。在 Java 7 更新 6 之后,Java 中的字符串不再使用偏移量并且未针对子字符串优化进行调整。相反,每个子字符串都会创建该字符串的新副本。

如果您想使用原始字符串实现,则必须使用 CharSequence。

欲了解更多信息:https ://jaxenter.com/the-state-of-string-in-java-107508.html

于 2017-08-03T16:48:01.263 回答