var num = words.stream().filter(w -> !w.isEmpty()).count();
This is shorter and closer to what you're expecting. The reason it's written the long way is because of the labels. The prior sentence is:A stream pipeline consists of three parts: a source of elements, any number of intermediate operations, and a terminal operation. For example: ...
so the goal here is to make the different components of the pipeline clear conceptually for the explanation of what's to come, not to write the shortest code possible. It therefore uses code that makes the underlying abstractions explicit rather than the sugar that avoids typing.
The reference to "expressive and efficient" meanwhile is likely about the fact that Java can parallelize streams in many cases (efficient). As for "expressive", well, that's somewhat debatable but for sure there are some cases where the functional pipeline style is easier to read and more expressive than the imperative style. In my own code I use both because indeed especially for short operations over lists and the like, sometimes a for loop is just clearer to my eye. But it's a matter of taste.
BTW the same code written in Kotlin:
val num = words.filter { !it.isEmpty() }.count()
Very similar. You may slightly prefer the syntax or dislike the "magical" auto-naming of the lambda parameter, I've seen it argued both ways.----
https://docs.oracle.com/en/java/javase/21/docs/api/java.base...()