ラムダ関数でローカル変数を使用したいのですが、エラーが発生します。コードの1.ポイントと2.ポイントを参照してください。
class Foo {
int d = 0; // 1. It compiles, but ugly, 2. doesnt compile
public void findMax(List<List<Route>> routeLists) {
int d = 0; // 2.Error : Local variable dd defined in an enclosing scope must be final or effectively final
routeLists.forEach(e-> {
e.forEach(ee -> {
d+=ee.getDistance();
});
});
... doing some other operation with d
}
}
それらをグローバル変数として設定せずに使用するにはどうすればよいですか?
ストリームで使用するにはfinalである必要がありますであるため、intを変数として使用することはできません。
ただし、intをラップするクラスを作成できます。
次に、このクラスを保持する変数をfinalとして宣言します。
内部int変数の内容を変更します。
public void findMax(List<List<Route>> routeLists) {
final IntWrapper dWrapper = new IntWrapper();
routeLists.forEach(e-> {
e.forEach(ee -> {
dWrapper.value += ee.getDistance();
});
});
int d = dWrapper.value;
... doing some other operation with d
}
public class IntWrapper {
public int value;
}
forEach
はその仕事には間違ったツールです。
int d =
routeLists.stream() // Makes a Stream<List<Route>>
.flatMap(Collection::stream) // Makes a Stream<Route>
.mapToInt(Route::getDistance) // Makes an IntStream of distances
.sum();
または、ネストされたforループを使用します。
int d = 0;
for (List<Route> rs : routeLists) {
for (Route r : rs) {
d += r.getDistance();
}
}
ストリームで使用される関数では、副作用を持つことは推奨されていません(または禁止されていますか?)。
より良い方法は、
routeLists.stream()
.flatMapToInt(innerList -> innerList.stream()
.mapToInt(Route::getDistance))
.sum()
または
routeLists.stream()
.mapToInt(innerList -> innerList.stream()
.mapToInt(Route::getDistance).sum())
.sum()
1つ目は、サブリストごとに、距離のストリームを作成します。これらのストリームはすべてフラット化され、一度に合計されます。
2つ目は、各サブリストの合計を作成してから、それらすべての合計を再度加算します。
それらは同等である必要がありますが、パフォーマンスの点でどちらが優れているかはわかりません(つまり、ストリームをフラット化する方が合計よりもコストがかかるかどうか)。
リストをフラット化してから距離を取得して合計する3番目の方法は、 Andy Turnerの回答 でカバーされています。