プログラミングとかブログ

Unity/C#/SRPGStudio/RPGツクールMVの情報とかその他気になったことを調べて書きます。

【Java】C#からLINQを移植したら結構違った

C#からjavaに移植しようとしたときにLINQはどうするのかと思ったらstreamというものがあるんですね。
記法が若干違う(Whereがfilterとか)ぐらいで同じように使えるかと思ったら仕様がだいぶ違ったのでメモ。

C#のコード
List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);
intList.Add(3);

if (intList.Where(s => s >= 2).Count() > 0)
{
	string str="";
	intList.Where(s => s >= 2).Select(s => str += s.ToString());
}

リスト内に2以上の数字があったならそれらを文字列に変換してstrにつなげる処理です。
上記を実行しようとするとエラーだらけになりました。
主なエラーは3つ。

エラー1:記法が違う

ラムダ式C#では=>ですが、javaでは->です。
Whereはfilter、Selectはmapになります。
C#だとListからLINQが使えますが、Javaはstreamを生成する必要があります。

エラー2:ラムダ式内で変数は処理できない。

ラムダ式の中でfinal変数以外を処理できないようです。以下のエラーが出ます。
Variable used in lambda expression should be final or effectively final

エラー3:streamが途中で止まる

java.lang.IllegalStateException: stream has already been operated upon or closed
ググってみるとstreamは終端処理(countとかtoArrayとか)をしてしまうとそれ以上処理ができなくなるようです。
countの時点でstreamが閉じているので再度生成しないといけません。


上記を踏まえて修正すると以下のようなコードになりました。

List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
Stream<Integer> repetitionStream = intList.stream().filter(s -> s >= 2);

if (repetitionStream.count() > 0) {
    String str = "";
    repetitionStream = intList.stream().filter(s -> s >= 2);

    Integer[] arr = repetitionStream.toArray(Integer[]::new);
    for (Integer k : arr)
	str += k.toString();

  Log.d("result",str);
}

countの後で再度streamを生成しています。
ラムダ式内で変数処理ができないので拡張for文を使ってSelect処理の代わりをやってます。

結果

D/result: 23