3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

foreachでコレクションに格納しているStringは更新できない/不変性

Last updated at Posted at 2020-12-03

#はじめに
これは、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を覚えてすぐの初心者の方が、もしかしたらハマるかな?と思い記事にしました。 思い当たることがあれば幸いです。

3
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?