Reputation: 499
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
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
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