java 8 local variable in stream.foreach [duplicate]

China☆狼群 提交于 2019-12-10 17:29:31

问题


I would like to use local variables in lambda functions but I get error: Please see the 1. and 2. points in the code.

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
    }
}

How can I use them whitout setting them as global variables ?


回答1:


You can't use an int as variable because it must be final to be used in a stream.

But You can create a class wrapping the int.

Then declare the variable holding this class as final.

Changing the content of the inner int variable.

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;
 }



回答2:


forEach is the wrong tool for the job.

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();

Or just use nested for loops:

int d = 0;
for (List<Route> rs : routeLists) {
  for (Route r : rs) {
    d += r.getDistance();
  }
}



回答3:


Having side effects is discouraged (or even forbidden?) in the functions used in a stream.

A better way would be one of

routeLists.stream()
    .flatMapToInt(innerList -> innerList.stream()
        .mapToInt(Route::getDistance))
    .sum()

or

routeLists.stream()
    .mapToInt(innerList -> innerList.stream()
        .mapToInt(Route::getDistance).sum())
    .sum()

The first will, for each sublist, create a stream of distances. All these streams will become flattened and summed at once.

The second will create the sum of each sublist and then add all those sums together again.

They should be equivalent, but I am not sure if the one or other is better in terms of performance (i. e., if flattening the streams is more expensive than summing).

A third alternative of flattening the lists and then getting and summing the distances is covered by Andy Turner's answer.



来源:https://stackoverflow.com/questions/49273149/java-8-local-variable-in-stream-foreach

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!