it-swarm.com.ru

локальная переменная Java 8 в stream.foreach

Я хотел бы использовать локальные переменные в лямбда-функциях, но я получаю ошибку: Пожалуйста, смотрите пункты 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
    }
}

Как я могу использовать их без установки их в качестве глобальных переменных?

3
Csaba Prog

Вы не можете использовать 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;
 }
0
Davide Lorenzo MARINO

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

Или просто используйте вложенные для циклов:

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

Наличие побочных эффектов не рекомендуется (или даже запрещено?) В функциях, используемых в потоке.

Лучший способ был бы один из 

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

или же

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

Первый для каждого подсписка создаст поток расстояний. Все эти потоки станут плоскими и суммированными одновременно.

Второй создаст сумму каждого подсписка и затем снова сложит все эти суммы вместе.

Они должны быть эквивалентны, но я не уверен, что один или другой лучше с точки зрения производительности (то есть, если выравнивание потоков обходится дороже, чем суммирование).

Третий вариант сглаживания списков, а затем получения и суммирования расстояний покрывается ответом Энди Тернера .

1
glglgl