Учитывая следующий код

List<Double> radia = Arrays.asList(1.0, 1.3, 1.6, 1.9);
List<Ball> listOfBalls = new ArrayList<>();        
radia.forEach(radius -> listOfBalls.add(new Ball(radius)));

listOfBalls.stream().map(b -> b.getVolume())
                        .filter(d -> d>10)
                        .forEach(d -> pprint(d));

Как узнать, какой мяч печатается в последнем forEach? Я хотел бы иметь возможность напечатать что-то вроде

"Ball with radius " b.getRadius() + " has volume" + d
3
ShadowFlame 14 Мар 2018 в 13:52

2 ответа

Лучший ответ

Поскольку лямбда-выражения не могут присваиваться переменным вне их области видимости, вам придется использовать объект в более высокой области, чтобы сохранить результат.

Обратите внимание, что это не является предполагаемым использованием лямбда-выражений или API потоков. Если вы ищете единственный конечный результат, вы должны использовать findFirst или findAny следующим образом:

listOfBalls.stream().map(Ball::getVolume)
                        .filter(d -> d>10)
                        .findFirst();

Если вы ищете List из Balls, используйте Collectors.toList() так:

List<Ball> result = listOfBalls.stream().map(Ball::getVolume)
                        .filter(d -> d>10)
                        .collect(Collectors.toList());

На этом этапе вы можете просмотреть список и вывести то, что вам нужно. Потоки потребляются во время операции, что означает, что вы не можете использовать их после вызова forEach, списки не связаны этим ограничением.

3
A. Bandtock 14 Мар 2018 в 11:02

Вы можете добиться желаемого, не сопоставляя каждое Ball с его объемом, но при необходимости фильтруя:

listOfBalls.stream()
    .filter(b -> b.getVolume() > 10)
    .forEach(b -> System.out.println(
         "Ball with radius " + b.getRadius() + " has volume " + b.getVolume()));

ИЗМЕНИТЬ согласно комментарий:

Если дважды вызывать метод Ball.getVolume нежелательно (либо из-за дорогостоящих вычислений, либо из-за доступа к БД), вы можете передать результат этого метода вместе с соответствующим экземпляром Ball по потоку. Если вы используете Java 9+:

listOfBalls.stream()
    .map(b -> Map.entry(b, b.getVolume())) // perform expensive call here
    .filter(e -> e.getValue() > 10)
    .forEach(e -> System.out.println(
         "Ball with radius " + e.getKey().getRadius() + " has volume " + b.getValue()));

Если вы используете Java 8, вы можете использовать new AbstractMap.SimpleEntry<>(...) вместо Map.entry(...).

0
Federico Peralta Schaffner 15 Мар 2018 в 13:07