I often see code like this:
employees.stream().map(e -> e.getName()).collect(Collectors.toList());
Code that creates a list with all the elements of the list named ʻemployees` translated into names. It's a normal code, but since the lambda expression is like an inner class, isn't there some overhead? If you just call such a getter,
employees.stream().map(Employee::getName).collect(Collectors.toList());
Isn't it faster?
I thought, I measured it to make sure. (Although it is a little old, it was measured with Oracle JDK 1.8u131.)
Using JMH, I measured the number of executions (throughput) of each code per second.
public class PerformanceMethods {
/**Generate 10000 elements*/
private static final List<Employee> employees = IntStream.range(0, 10000)
.mapToObj(i -> new Employee(i, "name" + i))
.collect(Collectors.toList());
@Benchmark
public void useReference() {
employees.stream().map(Employee::getName).collect(Collectors.toList());
}
@Benchmark
public void useLambda() {
employees.stream().map(e -> e.getName()).collect(Collectors.toList());
}
}
Result is···
Benchmark Mode Cnt Score Error Units
PerformanceMethods.useLambda thrpt 5 5842.820 ± 65.662 ops/s
PerformanceMethods.useReference thrpt 5 5762.353 ± 343.302 ops/s
The higher the Score, the faster the processing speed, but the result is almost the same. If you run it repeatedly, it doesn't matter which one you use.
By the way, as a reference, I will post a document that describes the mechanism of lambda expression execution. https://www.slideshare.net/miyakawataku/lambda-meets-invokedynamic
Normally, when you create an inner class, a class file is generated at compile time (class with "$"), but lambda expressions are initialized at runtime, so it seems to start faster. If you take it the other way around, it means that only the first timing to execute the lambda expression will be delayed by the amount of initialization.
I don't think it bothers me, but personally I would like to use a clean method reference.
When I was reviewing code made by others, I saw this code.
employees.stream().map(Employee::getAddress).map(Address::getPostalCode).collect(Collectors.toList());
For the first time, I saw how to use map
twice to convert when creating a list of values for nested entities. ..
Sure, I wrote that "I personally want to use a clean method reference", but it seems that map
twice is inefficient ...?
So, I measured the performance when writing map
once with one lambda expression and when writing map
twice with the method reference.
public class PerformanceMethods {
private static final List<Employee> employees = IntStream.range(0, 10000)
.mapToObj(i -> new Employee(i, "name" + i, new Address("code" + i)))
.collect(Collectors.toList());
@Benchmark
public void mapOnce() {
employees.stream()
.map(e -> e.getAddress().getCode())
.collect(Collectors.toList());
}
@Benchmark
public void mapTwice() {
employees.stream()
.map(Employee::getAddress)
.map(Address::getCode)
.collect(Collectors.toList());
}
}
Result is···
Benchmark Mode Cnt Score Error Units
PerformanceMethods.mapOnce thrpt 5 6340.454 ± 1291.055 ops/s
PerformanceMethods.mapTwice thrpt 5 5487.546 ± 488.373 ops/s
I'm curious that the lambda expression (mapOnce
) has a larger range of Error (variation in processing time), but I found that map
once is still faster.
map
It's easier to understand if you write the lambda expression once. ..
--There is almost no difference in speed between the method reference and the lambda expression. However, since the lambda expression is initialized at runtime, there is a little overhead at the beginning.
--It's faster to write a map
once using a lambda expression than to take a value using a method reference twice. And easy to read.
Recommended Posts