anton4o
anton4o

Reputation: 499

Java 8 nested loops to stream

Trying to get my head round the Java 8 streams syntax with a simple example. Had a look at the other similar questions on this topic, but could not find any solutions that would match my example and would work for me. Basically I am trying to refactor the following snippet with two nested loops to use the new stream API:

List<Car> filteredCars = new ArrayList<>();

for (Car car : cars) {
    for (Wheel wheel : wheels) {
        if (car.getColor() == wheel.getColor() &&
                wheel.isWorking() == true ) {
            filteredCars.add(car);
            break;
        }
    }
}

return filteredCars;

Managed to come up with this which returns void:

return cars.stream().forEach(
    car -> wheels.stream()
        .filter(wheel -> wheel.getColor() == car.getColor() &&
                    wheel.isWorking() == true)
        .collect(Collectors.toList()));

What is wrong with the stream syntax above and what am I missing?

Upvotes: 29

Views: 59959

Answers (2)

fabian
fabian

Reputation: 82491

The problem is, you're creating the List(s) inside the forEach and forEach returns void. This would be the equivalent of the following for loop:

for (Car car : cars) {
    List<Car> filteredCars = new ArrayList<>();
    for (Wheel wheel : wheels) {

        if (car.getColor() == wheel.getColor() &&
                wheel.isWorking() == true ) {

            filteredCars.add(car);
            break;
        }
    }
}

return filteredCars; // whoops cannot be accessed (scope) !!!

You could use filter on the cars stream and collect the use collect on the filtered stream to achieve the desired results:

Predicate<Car> carCheck = car -> wheels.stream().anyMatch(wheel -> car.getColor() == wheel.getColor() && wheel.isWorking());

List<Car> filteredCars = cars.stream().filter(carCheck).collect(Collectors.toList());

Upvotes: 5

Eran
Eran

Reputation: 394126

You can't perform two terminal operations - forEach and collect on the same Stream.

instead, you need to filter the cars list by checking for each car if it has a matching working wheel :

List<Car> filteredCars =
    cars.stream()
        .filter (
            car -> wheels.stream()
                         .anyMatch(wheel -> wheel.getColor() == car.getColor() &&      
                                            wheel.isWorking()))
        .collect(Collectors.toList());

Upvotes: 33

Related Questions