3

I am new to Java 8 and getting a bit confused about the scope of Lambda Expression. I read some articles expressing that Lambda Expression reduces execution time, so to find the same I wrote following two programs

1) Without using Lambda Expression

import java.util.*;

public class testing_without_lambda
{
  public static void main(String args[])
  {
    long startTime =  System.currentTimeMillis();       
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    for (int number : numbers) 
    {
        System.out.println(number); 
    }   
    long stopTime =  System.currentTimeMillis();
    System.out.print("without lambda:");
    System.out.println(stopTime - startTime);
  }//end main
}

output:

enter image description here

2) With using Lambda Expression

import java.util.*;
public class testing_with_lambda
{
  public static void main(String args[])
  {  
    long startTime =  System.currentTimeMillis();
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);    
    numbers.forEach((Integer value) -> System.out.println(value));  
    long stopTime =  System.currentTimeMillis();
    System.out.print("with lambda:");
    System.out.print(stopTime - startTime);
  }//end main
}

output:

enter image description here

Is this means Lambda Expression requires more time to execute?

Shiva
  • 143
  • 1
  • 7

3 Answers3

5

There is no general statement about “execution time” possible, as even the term “execution time” isn’t always meaning the same. Of course, there is no reason, why just using a lambda expression should reduce the execution time in general.

Your code is measuring the initialization time of the code and it’s execution time, which is fair, when you consider the total execution time of that tiny program, but for real life applications, it has no relevance, as they usually run significantly longer than their initialization time.

What make the drastic difference in initialization time, is the fact that the JRE uses the Collection API itself internally, so its classes are loaded and initialized and possibly even optimized to some degree, before your application even starts (so you don’t measure its costs). In contrast, it doesn’t use lambda expressions, so your first use of a lambda expression will load and initialize an entire framework behind the scenes.

Since you are usually interested in how code would perform in a real application, where the initialization already happened, you would have to execute the code multiple times within the same JVM to get a better picture. However, allowing the JVM’s optimizer to process the code bears the possibility that it gets over-optimized due to its simpler nature (compared to a real life scenario) and shows too optimistic numbers then. That’s why it’s recommended to use sophisticated benchmark tools, developed by experts, instead of creating your own. Even with these tools, you have to study their documentation to understand and avoid the pitfalls. See also How do I write a correct micro-benchmark in Java?


When you compare the for loop, also known as “external iteration” with the equivalent forEach call, also known as “internal iteration”, the latter does indeed bear the potential of being more efficient, if properly implemented by the particular Collection, but its outcome is hard to predict, as the JVM’s optimizer is good at removing the drawbacks of the other solution. Also, your list is far too small to ever exhibit this difference, if it exists.

It also must be emphasized that this principle is not tight to lambda expressions. You could also implement the Consumer via an anonymous inner class, and in your case, where the example suffers from the first time initialization cost, the anonymous inner class would be faster than the lambda expression.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
3

In Addition to Holger's answer I want to show how you could've benchmarked your code.

What you're really measuring is initialization of classes and system's IO (i.e. class loading and System.out::println).

In order to get rid of these you should use a benchmark framework like JMH. In addition you should measure with multiple list or array sizes.

Then your code may look like this:

@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@Measurement(iterations = 10, timeUnit = TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

  @Param({ "10", "1000", "10000" })
  private int length;

  private List<Integer> numbers;

  @Setup
  public void setup() {
    Random random = new Random(length);
    numbers = random.ints(length).boxed().collect(Collectors.toList());
  }

  @Benchmark
  public void externalIteration(Blackhole bh) {
    for (Integer number : numbers) {
      bh.consume(number);
    }
  }

  @Benchmark
  public void internalIteration(Blackhole bh) {
    numbers.forEach(bh::consume);
  }
}

And the results:

Benchmark                      (length)  Mode  Cnt      Score     Error  Units
MyBenchmark.externalIteration        10  avgt   30     41,002 ±   0,263  ns/op
MyBenchmark.externalIteration      1000  avgt   30   4026,842 ±  71,318  ns/op
MyBenchmark.externalIteration     10000  avgt   30  40423,629 ± 572,055  ns/op
MyBenchmark.internalIteration        10  avgt   30     40,783 ±   0,815  ns/op
MyBenchmark.internalIteration      1000  avgt   30   3888,240 ±  28,790  ns/op
MyBenchmark.internalIteration     10000  avgt   30  41961,320 ± 991,047  ns/op

As you can see there is little to no difference.

Community
  • 1
  • 1
Flown
  • 11,480
  • 3
  • 45
  • 62
1

I don't think lambda expression code will be faster in execution all the time, it would really depend on different conditions. Can you may be point me to the article where you read that the lambda expression is faster in execution time? (It certainly is considered faster while writing the code, due to functional programming style of code)

I ran your test again locally, found something strange:

  • The first time I ran the code without lambda, it took almost the same time, rather more as with the lambda. (49 milliseconds)
  • Second time onward, the code without lambda ran very much faster i.e. in 1 millisecond.
  • The code with lambda expression runs in the same amount of time every time, I tried around 3-4 times in total.

General Results:

1 2 3 4 5 6 with lambda:47

1 2 3 4 5 6 without lambda:1

I think it would really take a very large sample of numbers to really test this and also multiple calls to the same code to remove any initialization burden on the JVM. This is a pretty small test sample.

A_C
  • 905
  • 6
  • 18