Java配列を個別インスタンスでまとめて初期化する方法。ただしfor文テメーはダメだ

# TL;DR;

配列要素にインスタンスを埋めて初期化する時は、**無難に**`for`文を使うか`Arrays#setAll`を使おう。
`Arrays#fill`だとインスタンスが同一になる罠があるから気をつけてね。[^1]

[^1]:`Arrays.fill`で小ハマりしつつ、日本語の関連記事がうまく見つけられなかったので書きました。

# この記事を作った経緯(長い茶番とも言う)

本稿には挑戦的なタイトル[^2]や言葉の乱れがございますので、我慢するかブラウザバックをお願いいたします。

[^2]:書きながらテンションが上がって文体に乱れが出た点や、挑発的なタイトルになった点は書きながらテンションが上がったせいです。

## for文なんてダセーよなー

自作クラスの配列を初期化する時、どうしますか？
Javaの教本では`for`文使うじゃないですか。
例えば★こんな風★に。

```
public class Sample {
    public static void main(String[] args) {
        Sample[] samples = new Sample[3];
        // ★こんな風★
        for(int i = 0; i < 3; i++) {
            samples[i] = new Sample();
        }
    }
}
```

ですけどねぇ…エレガントなコード階級[^3]になりますとね、まるで我慢ならないのよ。
こんな**Java1.4臭がするロートルなコード**は。

ぐるぐるループ回してて、全然スタイリッシュじゃありませんわー。
ブレークポイント当てたら3回も止まるんですのよ！

おお嫌だ嫌だ。こんなコードはどこかに捨てておしまい。

[^3]:なお私は**業務でほとんどJavaを使わない**ため、エレガントなコード階級はあくまでも自称行為です。

## コピペじゃない、コピペじゃない、Don'tのコードさ

だったらこうすればいい？

```
        Sample[] samples = new Sample[] { new Sample(), new Sample(), new Sample() };
```

おおお…肌が粟立って二の句が継げないわ。
「`for`文使うな」と言われて「はいはい、じゃあコピペしてやりますよ」とかトンチキなトンチを利かせたつもりかしら？
これはトンチじゃなくてトンマって言うのよ、この○○○○○！

いいこと？
仕様変更で配列が3つから30個に増えたらどうするつもりかしら。
`new Sample();`を無作為の回数コピペするコーダーと、コピペされた個数を数えてからクレンジングするデータサイエンティストを投入しないといけないわ。
そしたら`for`文でも1人で済んでた作業に10人くらい必要かもしれないわね。**100倍ですわよ100倍！**

## IntStream.rangeすればいいと思った？

まあ、Java8に慣れてきたご令嬢は★ナウなヤング風★に`IntStream`を使うらしいじゃない？

```
import java.util.stream.IntStream;

public class Sample {
    public static void main(String[] args) {
        Sample[] samples = new Sample[3];
        // ★ナウなヤング風★
        IntStream.range(0, 3).forEach(i -> samples[i] = new Sample());
    }
}
```

でもアナタ、`forEach`の`i`がループっぽく見えますのよ！残念！！アロー演算子切り！！
ここまでのコードに比べればいくらかお上品ですけれど、私ならばもっと瀟洒なコードを選びますわよ。
次節で見せて差しあげますわ。せっかく！！[^4]ですので。

[^4]:切腹！！の誤字ではありません。本稿と一切関係ありませんが、**令和生まれ**の読者には波田陽区さんをご存じない方もおられるかもしれませんね。

## Arrays.fillじゃダメだったよ

私がこの記事を書く経緯となった、ご自慢のコードは★こちら★ですわ～！

```
import java.util.Arrays;

public class Sample {
    public String name;  // publicフィールドはお行儀悪いけどごめんあそばせ

    public static void main(String[] args) {
        Sample[] samples = new Sample[3];
        // ★こちら★
        Arrays.fill(samples, new Sample());

        samples[0].name = "hoge";
        samples[1].name = "fuga";
        samples[2].name = "piyo";
        
        for(Sample sp: samples) {
            System.out.println(sp.name);
        }
    }
}
```

ほらごらんなさい！
なんて**ミスする余地なく**、**可読性に優れた**素晴らしいコードなのかしら。
`Arrays#fill`を使えば配列の各要素を同一の内容でフィりますのよ！

例えば下のようにコーディングすれば`801`個の`arr`変数すべてに`11`が入るのですわよ。

```
int[] arr = new int[801];
Arrays.fill(arr, 11);
```

つまり簡単に、やおい じゅういち ができるのよ。
満を持して先ほどの`Sample`コードを実行しますわよ…それ！

**実行結果**

```
piyo
piyo
piyo
```

**ゆーふぉー！**
ななな、なぜ？どうして？**Why！Java needs Why！**

### アカン考察法

こほん。
私の心酔している[StackOverflow様](https://stackoverflow.com/a/36098384/8248751)によると、`Arrays.fill`は第二引数のインスタンスをすべての配列要素に等しく詰め込んでるようですわ。

```
public static void fill(Object[] a, Object val) {
    for (int i = 0, len = a.length; i < len; i++)
        a[i] = val;
}
```

これでは同一のインスタンスを見てしまいますわね…。
まるで複数のインスタンスを作ったつもりが、インスマスみたく架空のインスタンスで満たされたかのようですわ。

あら？この例えでは何言ってるか分からない？
平気ですわ。**私も分かりません**もの。

## Collections.nCopiesも参照先は同一っぽい

ちなみに下記のように書き換えてもぜんぶpiyoりましたわ。

```
import java.util.Collections;
(中略)
        // これを
        Arrays.fill(samples, new Sample());
        // こうする
        samples = Collections.nCopies(samples.length, new Sample()).toArray(new Sample[samples.length]);
```

**実行結果**

```
piyo
piyo
piyo
```

`Collections#nCopies`してもシャローコピーなのは意外でしたの。
結局のところ、シャロー様とは浅薄な関係だったってことなのね。
まあ、実在しない人名をかたっても**この記事の情報量は砂糖一粒ほども増えません**けど。

# 成功事例

## Stream.generateで解決できる

試行錯誤の過程で、下のように書き換えてみましたわ。

```
import java.util.stream.Stream;
(中略)
        // これを
        Sample[] samples = new Sample[3];
        Arrays.fill(samples, new Sample());
        // こうする
        Sample[] samples = Stream.generate(Sample::new).limit(3).toArray(Sample[]::new);
```

**実行結果**

```
hoge
fuga
piyo
```

やりました！やりましたわー！
やっぱり時代はメソッドチェーンですのね！
`Stream#generate`って`Supplier`関数型インタフェースを使うと無限にインスタンスを作れますのね。
末端の技術者にとっては、ブロックチェーンみたいな難しい理論よりもメソッドチェーンの方が安くて速くて旨い**サプライチェーンやー**ですわ！
ですけど、わざわざ`limit`して`toArray`するよりも簡潔な方法はないかしら。

## もうArrays.setAllでいいんじゃないかな

そんなこんなであれやこれやがてんやわんやした結果、★かくかくしかじか★に落ち着きましたわ。

```
import java.util.Arrays;

public class Sample {
    public String name;  // publicフィールドはお行儀悪いけどごめんあそばせ

    public static void main(String[] args) {
        Sample[] samples = new Sample[3];
        // ★かくかくしかじか★
        Arrays.setAll(samples, i -> new Sample());

        samples[0].name = "hoge";
        samples[1].name = "fuga";
        samples[2].name = "piyo";
        
        for(Sample sp: samples) {
            System.out.println(sp.name);
        }
    }
}
```

**実行結果**

```
hoge
fuga
piyo
```

エレガントでしょう？**おーっほほほほ！**
`Arrays#setAll`を使うことで、第二引数の`IntFunction`ごとに独立したインスタンスを作成できますのよ！

え？アロー演算子切り？
そんな昔のことは忘れましたわよ！
これを使えばワンライナーで初期化できますし、きめ細かな処理すら可能ですわ。
これを活用すれば、**`for`文を使った初期化と同等**の初期化処理ができますわね！

この記事の結論をズバリ言いますわよ！！

結局のところ`for`文も捨てたものじゃありませんわね…ほとんど**誰でも読めて保守性が高い**ですし。[^5]
そして`Arrays#fill`みたいな**使いなれないコード**を無意味に使うと、罠にハマって無駄に疲れることがありますのでお気をつけあそばせ。
そう、謎のお嬢様言葉を使って**無駄に疲弊**している今の私のように！

[^5]:`for`文が読めない人も一定数いますが本稿では議論しません

# 参考資料

[Arrays.fill() uses the same object all time](https://stackoverflow.com/a/36098384/8248751)
[Create an array with n copies of the same value/object?](https://stackoverflow.com/a/27920829/8248751)
[Stream.generate](https://stackoverflow.com/a/19115815/8248751)

[IntStream.range](https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/IntStream.html#range-int-int-)
[Arrays.fill](https://docs.oracle.com/javase/jp/8/docs/api/java/util/Arrays.html#fill-java.lang.Object:A-java.lang.Object-)
[Arrays.setAll](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#setAll-T:A-java.util.function.IntFunction-)
[Collections.nCopies](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#nCopies%28int,%20T%29)