しろあんのさかな

ふーちゃのエンジニアブログ

Javaの拡張for文(for-each文)は値渡しだった

最近Javaの勉強を兼ねてAtCoderをはじめた私がプログラムを書いててはまったところ。

Javaの拡張for文

Java5から導入された書き方。コレクションのオブジェクト一つ一つに処理したい時に利用する書き方。通常のfor文でもかけるが、コードがスッキリするとされている。

間違い:参照渡しと信じきってた

以下は長さ5の配列を1で初期化するプログラムhoge.javaの一部

int[] array = new int[5];
for (int a : array) {
    a = 1;
    System.out.println(a);
}

結果は

$java hoge
1
1
1
1
1

期待通りで配列は1で初期化できているように見える。しかし、for文を抜けたあとに配列の要素をprintしてみると、

int[] array = new int[5];
for (int a : array) {
    a = 1;
    System.out.println(a);
}
for (int a : array) {
    System.out.println(a);
}
$java hoge
1
1
1
1
1
0
0
0
0
0

よって、配列は1で初期化できていない。

解決策

結局インデックスを指定して書き込むのが良さそう。拡張for文をそのまま使うにはfor文で使うカウンタ的なものを宣言しておく。

int[] array = new int[5];
int i = 0; //for文のカウンタ
for (int a : array) {
    array[i] = 1;
    System.out.println(array[i++]);
}
for (int a : array) {
    System.out.println(a);
}
$java hoge
1
1
1
1
1
1
1
1
1
1

ここまでくると普通のfor文のほうが読みやすいのかもしれない。 以下の記事ではfor文のカウンタスコープがfor文以外からも参照できるのがきになるとされている。 qiita.com

いろいろ試したもの

for文を抜けたあとのaの値を調べてみたかった

ガベージコレクションされてなかったらfor文後のint aのアドレスの中身が残ってるんじゃないか?

int[] array = new int[5];
for (int a : array) {
    a = 1;
    System.out.println(a);
}
System.out.println(a); // ここ
for (int a : array) {
    System.out.println(a);
}

結果は

エラー: シンボルを見つけられません
    System.out.println(a);
                       ^

コンパイルエラー。そりゃそうだ笑

for文を抜けたあとのaの値がみたい

int[] array = new int[5];
int a; // ここで宣言する
for (a : array) {
    a = 1;
    System.out.println(a);
}
System.out.println(a); 
for (int a : array) {
   System.out.println(a);
}

結果は

エラー: for-loopの不正な初期化子
    for (a : array) {
         ^
エラー1個

構文エラー。...だめか

java8からのforEach文も検証しようとした

lambda式の勉強からだなと思った。