IDEA suggests to replace, for example, this:
for (Point2D vertex : graph.vertexSet()) {
union.addVertex(vertex);
}
with this:
graph.vertexSet().forEach(union::addVertex);
This new version is sure much more readable. But are there situations when I'd better stick to the good old language construct for iteratables rather than using the new foreach
method?
For instance, if I understand correctly, the method reference mechanism implies constructing an anonymous Consumer
object that otherwise (with for
language construct) would not be constructed. Could that become a performance bottleneck for some actions?
So I wrote this not very exhaustive benchmark:
package org.sample;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;
import org.tendiwa.geometry.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class LanguageConstructVsForeach {
private static final int NUMBER_OF_POINTS = 10000;
private static final List<Point2D> points = IntStream
.range(0, NUMBER_OF_POINTS)
.mapToObj(i -> new Point2D(i, i * 2))
.collect(Collectors.toList());
@Benchmark
@Threads(1)
@Fork(3)
public void languageConstructToBlackhole(Blackhole bh) {
for (Point2D point : points) {
bh.consume(point);
}
}
@Benchmark
@Threads(1)
@Fork(3)
public void foreachToBlackhole(Blackhole bh) {
points.forEach(bh::consume);
}
@Benchmark
@Threads(1)
@Fork(3)
public List<Point2D> languageConstructToList(Blackhole bh) {
List<Point2D> list = new ArrayList<>(NUMBER_OF_POINTS);
for (Point2D point : points) {
list.add(point);
}
return list;
}
@Benchmark
@Threads(1)
@Fork(3)
public List<Point2D> foreachToList(Blackhole bh) {
List<Point2D> list = new ArrayList<>(NUMBER_OF_POINTS);
points.forEach(list::add);
return list;
}
}
And got:
Benchmark Mode Samples Score Error Units
o.s.LanguageConstructVsForeach.foreachToBlackhole thrpt 60 33693.834 ± 894.138 ops/s
o.s.LanguageConstructVsForeach.foreachToList thrpt 60 7753.941 ± 239.081 ops/s
o.s.LanguageConstructVsForeach.languageConstructToBlackhole thrpt 60 16043.548 ± 644.432 ops/s
o.s.LanguageConstructVsForeach.languageConstructToList thrpt 60 6499.527 ± 202.589 ops/s
How comes foreach
is more efficient in both cases: when I do virtually nothing and when I do some actual work? Doesn't foreach
simply encapsulate Iterator
? Is this benchmark even correct? If it is, is there any reason today to use the old language construct with Java 8?