文字列が不変である理由
文字列の不変性を理解します
「文字列が不変である理由」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
文字列は変更できない
Java の String は不変です。一度作成すると、その文字を変更することはできません。文字列を変更しているように見えるメソッドも、実際にはまったく新しい文字列を返します。
メソッドは新しい文字列を返す
toUpperCase() や replace() などのメソッドは、元の文字列を変更しません。新しい String を生成し、元の文字列はそのまま残します。
public class Main {
public static void main(String[] args) {
String original = "hello";
String upper = original.toUpperCase();
System.out.println("original: " + original);
System.out.println("upper: " + upper);
}
}再代入は変更ではない
s = s + "!" と記述すると、変数は新しい文字列を指すようになります。古い文字列オブジェクトは変更されず、ガベージコレクションの対象になる可能性があります。
public class Main {
public static void main(String[] args) {
String s = "hi";
String firstRef = s;
s = s + "!";
System.out.println("s: " + s);
System.out.println("firstRef: " + firstRef);
}
}なぜ不変なのか
不変性には実際の利点があります。
- スレッドセーフ:文字列をロックなしで共有できます
- キャッシュ:ハッシュコードやプール内のエントリが有効なまま保たれます
- 安全性:メソッドに渡した文字列が、意図せず変更されることはありません
文字列プール
同一の文字列リテラルは、文字列プール内の1つのオブジェクトを共有できます。文字列が決して変更されないからこそ、これが安全に行えます。ここでは、2つのリテラルが同じプール内のオブジェクトを参照しています。
public class Main {
public static void main(String[] args) {
String a = "java";
String b = "java";
System.out.println(a == b);
System.out.println(a.equals(b));
}
}new String はプール共有を解除する
new String(...) を使用すると別のオブジェクトが強制的に作成されるため、内容が同じでも参照の等価性は異なります。内容の比較には必ず equals を使用してください。
public class Main {
public static void main(String[] args) {
String a = "java";
String b = new String("java");
System.out.println("== : " + (a == b));
System.out.println("equals: " + a.equals(b));
}
}連結のコスト
文字列に対する各 + は、新しいオブジェクトを作成します。ループ内でこれを行うと、短時間しか使われない文字列が多数割り当てられるため、時間とメモリを無駄にします。
public class Main {
public static void main(String[] args) {
String result = "";
for (int i = 1; i <= 5; i++) {
result = result + i;
}
System.out.println(result);
}
}二次的な増加
連結するたびに、それまでのすべての文字がコピーされるため、ループ内で + を使って長い文字列を構築すると、規模が大きくなるにつれて効率が悪化します(およそ O(n²))。これが中心的なパフォーマンス上の問題です。
public class Main {
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 1000; i++) {
s += "x";
}
System.out.println("Length: " + s.length());
}
}不変な値は予測しやすい
文字列をメソッドに渡しても安全です。メソッドが元の文字列を変更することはできません。ここではメソッドが新しい文字列を作成し、呼び出し元の値はそのまま保たれます。
public class Main {
static void shout(String s) {
s = s + "!!!";
System.out.println("inside: " + s);
}
public static void main(String[] args) {
String msg = "hello";
shout(msg);
System.out.println("outside: " + msg);
}
}解決策の紹介
文字列を効率的に構築する必要がある場合、Java にはStringBuilderという可変版のクラスが用意されています。毎回新しいオブジェクトを割り当てるのではなく、その場で内容を変更します。
public class Main {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
sb.append(i);
}
System.out.println(sb.toString());
}
}2つの方法を比較する
連結が数回だけなら、+ で十分読みやすく記述できます。ループ内で何度も連結する場合は、StringBuilder を使用してください。構築する量に応じて選択しましょう。
public class Main {
public static void main(String[] args) {
String simple = "a" + "b" + "c";
StringBuilder sb = new StringBuilder("a");
sb.append("b").append("c");
System.out.println(simple);
System.out.println(sb);
}
}確認問題
String に対して s.toUpperCase() を呼び出すと、実際には何が起こるでしょうか。
まとめ
文字列が不変である理由を学びました。
Stringの内容は決して変更されません- メソッドは新しい文字列を返します
- 不変性によって、スレッドセーフ、プール共有、安全性が実現します
- ループ内で
+による連結を行うと、規模が大きくなるにつれて効率が悪化します StringBuilderが可変な解決策です
よくある質問
「文字列が不変である理由」レッスンは無料ですか?
はい。「文字列が不変である理由」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「文字列が不変である理由」で何を学びますか?
文字列の不変性を理解します ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「文字列が不変である理由」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。