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 ビット モードの場合は、カウントする必要があります: 参照 "value[]" に 4 バイト、オフセットに 4 バイト、カウントに 4 バイト、クラス String のすべてのインスタンスのハッシュに 4 ? これは、「String x;」と書くことを意味します。私のクラスの1つで、クラスの「重み」に少なくとも32バイトが自動的に追加されます(おそらくここで間違っています)。

4

4 に答える 4

3

intern()オフセット/カウント フィールドは、プーリング/問題と多少直交しています。オフセットとカウントは、次のような場合に発生します。

String substring = myString.substring(5);

このメソッドを実装する 1 つの方法は次のようになります。

  • char[]新しいwithmyString.length() - 5要素を割り当てる
  • インデックス index 5myString.length()から myString までのすべての要素を new にコピーしますchar[]
  • substringは、この新しいchar[]
    • substring.charAt(i)に直接行くchars[i]
    • substring.length()に直接行くchars.length

ご覧のとおり、このアプローチは O(N) (N は新しい文字列の長さ) であり、新しい文字列と新しい char[] の 2 つの割り当てが必要です。代わりに、substring元の char[] を再​​利用することで機能しますが、オフセットがあります。

  • substring.offset=myString.offset + newOffset
  • substring.count=myString.count - newOffset
  • myString.charsのchars配列として使用substring
    • substring.charAt(i)に行くchars[i+substring.offset]
    • substring.length()に行くsubstring.count

新しい char[] を作成する必要がなかったことに注意してください。さらに重要なことに、古い char[] から新しいものに文字をコピーする必要もありませんでした (新しいものがないため)。したがって、この操作は単なる O(1) であり、必要な割り当ては新しい文字列の割り当てだけです。

于 2012-08-17T16:52:57.313 に答える
2

Javaは常に任意のオブジェクトへの参照を使用します。参照を使用しないようにする方法はありません。文字列プーリングに関しては、これは文字列リテラルのコンパイラによって、実行時にを呼び出すことによって実現されますString.internStringの実装のほとんどは、定数プールによって参照されるインスタンスを処理しているかどうかに気付かないのは当然です。

于 2012-08-17T16:23:38.773 に答える
1

Java 文字列は不変です。これは、アプリケーション コードを壊すことなく、実装が内部表現に対して多くのことを実行できることを意味します。

JavaString.intern()は、Oracle の JDK 実装でネイティブに定義されていることに注意してください。ネイティブ コードは、オブジェクトのすべてのフィールドにアクセスでき、水中で参照を変更する可能性があります。したがって、実装者がしなければならないことは、文字列がインターンされている場所への参照とオフセットを変更することだけです。もちろん、これはクラスの不変性を壊すので、これは intern() 更新がよりスレッドセーフであることを意味します。

intern()新しく生成された文字列を呼び出すと、フィールドがどうなるかを確認できます。何も起こらない場合は、代わりに参照自体にメモリの場所が含まれている可能性があります。Java 言語仕様では、参照の実装方法は定義されていません。

于 2012-08-17T16:40:11.023 に答える