I was very lukewarm about lambdas in Java 8, but all the changed when I looked under the hood.
Java 8 lambdas make really great use of invokedynamic. In Java 7 invokedynamic was originally introduced to help dynamically typed languages.
Soon the designers of Java realized that compiling lambdas to inner classes is not going to help. It was quite a surprise that they finally decided to
reimplement invokedynamic in a way that the biggest user of this feature now is Java.
So, one efficiency comes from invokedynamic. The other efficiency comes from the lazy evaluation capability built into Streams.
Take a look under the hood, run some prototypes, and see how it measures up.