#はじめに
これは、Stringのコレクションを拡張for文、ForEachでは「内容の更新」が出来ないというお話です。
##どんな時か
以下のコードで、strArrayに放り込んだ"a","b","c"に"X"という文字を連結し、その内容をforeachを抜けた後で出力します。
private void modifyStrInCollection() {
String strArray[] = { "a", "b", "c" };
///foreachで更新。
for (String str : strArray) {
str += "X";
System.out.println("01:" + str);
}
///配列の内容出力
for (String str : strArray) {
System.out.println("02:" + str);
}
}
どんな結果になると思いますか? "aX","bX","cX"になっているでしょうか。 実行結果は以下の通りです。
01:aX
01:bX
01:cX
02:a
02:b
02:c
更新しているforeachの中ではstrに"X"が連結されていますが、外に出ると元に戻っています。 なぜでしょうか。
##Stringクラスの「不変性(immutable)」が、その理由
簡単に言えば、「文字列を更新しているようで、していない。 新しく文字列オブジェクトを作成する」のです。 詳細は「String 不変性」で検索していただけたらと思います。
foreachは、コレクションの最初から一つずつ要素の値を抜き出しますが、Stringは「参照型」です。 そして、Stringは不変であるため、更新した場合**「元の文字列はそのまま、新しい文字列オブジェクトを作成し、その新しい参照を返す」**のです。
つまり、配列から要素の参照を抜き出すまではいいのですが、それを更新しても元の文字列は変わらず
str += "X";
の[str]には、配列の中にあった参照とは別ものが設定されています。 やっていることは
String modifiedStr = str + "X";
と同じで、元のstrは何の影響も受けません。
##Stringを内部に持っているクラスなら問題なく更新する
当然ですが、Stringをメンバに持つクラスをforeachにかけ、メンバとしてのStringを更新することはできます。 Stringに限らず、不変性の性質を持つクラスはforeachで(直接)更新できないというだけです。
##おわりに
foreachを覚えてすぐの初心者の方が、もしかしたらハマるかな?と思い記事にしました。 思い当たることがあれば幸いです。