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?
Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.
The short version basically is, if you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better. And since parallel streams have quite a bit of overhead, it is not advised to use these unless you are sure it is worth the overhead.
If you have a small list, loops perform better. If you have a huge list, a parallel stream will perform better. Purely thinking in terms of performance, you shouldn't use a for-each loop with an ArrayList, as it creates an extra Iterator instance that you don't need (for LinkedList it's a different matter).
Java, like most other programming languages, supports nested loops. This means just a loop within a loop.
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());
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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With