Close

Capturing vs Non-Capturing Lambdas

[Updated: Feb 10, 2016, Created: Feb 10, 2016]

A lambda expression is said to be capturing if it either access instance variables of it's enclosing class or local variables (final or effectively final) from it's enclosing scope.


A non-capturing lambda doesn't use any variables from outside.


Non-capturing lambdas generally are considered more efficient than the capturing ones as they are evaluated only once.


Let's do some quick tests on different scenarios and see which one performs better.


Lambda capturing local final variables:

     final BigDecimal bd = new BigDecimal(1);
     Function<BigDecimal, BigDecimal> func = (a) -> bd.multiply(a);

     for (int j = 0; j < 999999999; j++) {
            func.apply(new BigDecimal(j));
     }

Average time taken: 1725 ms


Lambda capturing local effectively final variables:

      BigDecimal bd = new BigDecimal(1);
      Function<BigDecimal, BigDecimal> func = (a) -> bd.multiply(a);

      for (int j = 0; j < 999999999; j++) {
            func.apply(new BigDecimal(j));
      }

Average time taken: 1688 ms


Lambda instance variable capturing

public class LambdaInstanceCapturing implements Runnable {

    private BigDecimal bd = new BigDecimal(1);

    @Override
    public void run() {
        Function<BigDecimal, BigDecimal> func = (a) -> bd.multiply(a);

        for (int j = 0; j < 999999999; j++) {
            func.apply(new BigDecimal(j));
        }
    }
}

Average time taken: 1750 ms


Non-capturing Lambda

In this example we are just multiplying the passed BigDecimal variable with constant BigDecimal.TEN. It's still not accessing anything from outside as static final constants are inlined by the compiler .

   Function<BigDecimal, BigDecimal> func = (a) -> a.multiply(BigDecimal.TEN);

        for (int j = 0; j < 999999999; j++) {
            func.apply(new BigDecimal(j));
        }

Average time taken: 1651 ms

Conclusion:

Non-capturing Lambda is performing little better than others. But I won't suggest to make decisions based on that as things are changing with each minor release of Java 8. It's generally good to know all aspects of lambdas though.

Example Project

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.0.4

Java Capturing Lambda Test Select All Download
  • capturing-lambda-tests
    • src
      • main
        • java
          • com
            • logicbig
              • example

The class MainBenchmarkTest runs each test 5 times and output the average.

Output:

Lambda local final capturing  time: 1725
Lambda local effectively final capturing  time: 1688
Lambda instance capturing  time: 1750
Lambda non capturing  time: 1651

The Java version I used

c:\>java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
System info:
C:\>systeminfo
...
OS Name:                   Microsoft Windows 8.1
OS Version:                6.3.9600 N/A Build 9600
OS Configuration:          Standalone Workstation
OS Build Type:             Multiprocessor Free
.....
System Type:               x64-based PC
Processor(s):              1 Processor(s) Installed.
[01]: Intel64 Family 6 Model 71 Stepping 1 GenuineIntel ~2701 Mhz
....
Total Physical Memory:     16,299 MB
Available Physical Memory: 8,893 MB
Virtual Memory: Max Size:  18,752 MB
Virtual Memory: Available: 9,204 MB
Virtual Memory: In Use:    9,548 MB

See Also