Javaで文字列を連結すると、メモリにいくつの文字列が作成されますか?


17

Javaの不変文字列について尋ねられました。文字列に多数の "a"を連結する関数を書くことを任されました。

私が書いたもの:

public String foo(int n) {
    String s = "";
    for (int i = 0; i < n; i++) {
        s = s + "a"
    }
    return s;
}

その後、ガベージコレクションが発生しないと仮定して、このプログラムが生成する文字列の数を尋ねられました。n = 3に対する私の考えは

  1. 「」
  2. 「a」
  3. 「a」
  4. 「aa」
  5. 「a」
  6. 「aaa」
  7. 「a」

基本的に、ループの各反復で2つの文字列が作成されます。ただし、答えはn 2でした。この関数によってメモリ内に作成される文字列は何ですか?その理由は何ですか?


15
この仕事を提供されたら、逃げて、非常に速く走って
ください.......-mattnz

@mattnzには複数の理由があります(コードの記述だけではありません)。

3
JITがループを最適化しない限り、これにはO(n ^ 2)ランタイムがかかりますが、n ^ 2文字列は作成されません。
user2357112は、Monicaを

回答:


26

その後、ガベージコレクションが発生しないと仮定して、このプログラムが生成する文字列の数を尋ねられました。n = 3に対する私の考えは(7)

文字列1("")および2("a")はプログラム内の定数です。これらは物の一部として作成されるのではなく、コンパイラが知っている定数であるため、「インターン」されます。詳細については、WikipediaのString interningを参照してください。

また、文字列5と7は文字"a"列#2 と同じであるため、カウントから削除されます。これにより、ストリング#3、#4、および#6が残ります。答えは、コードを使用して「n = 3に対して3つの文字列が作成されます」です。

n個のカウント2は、N = 3で、これは9になり、さらには自分の非インターン文字列が正しい場合のみ7.だったあなたの最悪の場合の答えで、答えがあるため、明らかに間違っているはずです 2N + 1となっています。

だから、これをどのように行うべきかという質問はありますか?

Stringは不変なので、変更可能なものが必要です。これは、新しいオブジェクトを作成せずに変更できるものです。それがStringBuilderです。

最初に確認するのはコンストラクターです。この場合、文字列がどれくらいの長さになるかがわかっStringBuilder(int capacity) ており、必要なだけ正確に割り当てることを意味するコンストラクターがあります。

次に、Stringである"a"必要はありませんが、characterにすることができます。これは、呼び出し時に高めるいくつかのマイナーな性能がある対を-と、この方法は、文字列がどのくらい見つけると、その上のいくつかの作業を行う必要があります。一方、は常に正確に1文字の長さです。'a'append(String)append(char)append(String)char

コードの違いは、StringBuilder.append(String) vs StringBuilder.append(char)で確認できます。そのない何かすることがあまりにもに関係しますが、それは可能な限り最高のプラクティスを使用するのが最善である雇用者を感動しようとしている場合。

それで、あなたはそれをまとめるとき、これはどのように見えますか?

public String foo(int n) {
    StringBuilder sb = new StringBuilder(n);
    for (int i = 0; i < n; i++) {
        sb.append('a');
    }
    return sb.toString();
}

1つのStringBuilderと1つのStringが作成されました。追加の文字列をインターンする必要はありません。


Eclipseで他のいくつかの簡単なプログラムを作成します。pmdをインストールし、作成したコードで実行します。文句を書き、それらを修正します。これは、ループ内で+付き文字列の変更を発見しただろう、とあなたはStringBuilderのにそれを変更した場合、それはいるだろう多分初期容量を見つけたが、それは確かに違いをキャッチだろう.append("a")し、.append('a')


9

反復ごとにString+演算子によって新しいが作成され、に割り当てられsます。戻った後、最後のもの以外はすべてガベージコレクションされます。

"""a"などの文字列定数は毎回作成されるわけではなく、これらはインターンされた文字列です。文字列は不変であるため、自由に共有できます。これは文字列定数に起こります。

文字列を効率的に連結するには、を使用しますStringBuilder


インタビューの人々は、実際にリテラルが存在するかどうかについて議論し、リテラルは毎回作成されると決めました。しかし、これは理にかなっています。
アハルバート

6
どのように言語が何をするのかを「議論」します。確かに仕様を読んで確実に知っている、または定義されていないため、正しい答えがありません
。....-mattnz

@mattnz実装の詳細に関しても、使用しているコンパイラ/ランタイムが何をしているのかを知ることは興味深いかもしれません。これは特にパフォーマンスに当てはまります。
svick

1
@svick:仮定を立てることで多大な利益を得ることができ、その後コンパイラがアップグレードされ、最適化が変更されるなど。定義された動作ではなく不特定の動作に依存しているため、動作が変化してバグが発生します。最適化について彼らが言うことを知っている-a)それを専門家に任せ、b)あなたはまだ専門家ではない。:)パフォーマンスにのみ依存しているが、それでも言語仕様に依存している場合、パフォーマンスが低下するだけです。未指定またはコンパイラ固有の動作に依存するコードが予期しない方法で破損することがよくあります(主にCおよびC ++)。
-mattnz

@mattnzでは、パフォーマンスに関連する意思決定をどのように提案しますか?通常、仕様/ドキュメントから得られる最高のものは、大きなOの複雑さですが、それだけでは十分ではありません。いずれにせよ、パフォーマンスは常に実装に依存するため、パフォーマンスに関しては実装の詳細に依存してもかまいません。
svick

4

MichaelTが答えで説明しているように、あなたのコードはO(n)文字列を割り当てます。ただし、O(n 2)バイトのメモリも割り当て、O(n 2)時間で実行します。

O(n 2)バイトを割り当てます。割り当てる文字列の長さは0、1、2、…、n-1、nであり、合計は(n 2 + n)/ 2 = O(n 2)になります。

i番目の文字列を割り当てるには、長さi-1の(i-1)番目の文字列をコピーする必要があるため、時間もO(n 2)です。これは、割り当てられた各バイトをコピーする必要があることを意味し、O(n 2)時間かかります。

たぶん、これはインタビュアーが意味したものですか?


ここのように、方程式は(n ^ 2 + n)/ 2であってはなりませんか?
-HeyJude
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.