12

Lets assume i want to iterate over a collection of objects.

List<String> strmap = ...
//Without lambdas
strmap.stream().filter(new Predicate<String>() {
      public boolean test(String string) {
          return string.length == 10;
      }
}.forEach(new Consumer<String>() {
      public void accept (String string) {
          System.out.println("string " + string + " contains exactly 10 characters");
      }
} 
//With lambdas
strmap.stream()
      .filter(s -> s.length == 10)
      .forEach(s -> System.out.println("string " + s + " contains exactly 10 characters");

How does the second example (without lambdas) work? A new object (Predicate and Consumer) created every time i call the code, how much can java jit compiler optimalize a lambda expression? For a better performace should i declare all lambdas as a variable and always only pass a reference?

private Predicate<String> length10 = s -> s.length == 10;
private Consumer<String> printer = s -> { "string " + s + " contains exactly 10 characters"; }

strmap.stream()
      .filter(length10)
      .forEach(printer);
Pshemo
  • 122,468
  • 25
  • 185
  • 269
user2749903
  • 1,275
  • 1
  • 10
  • 20
  • 3
    See [here](http://stackoverflow.com/q/27524445/2711488). And [this](http://stackoverflow.com/a/23991339/2711488) also applies to lambda expressions as well as method references. HotSpot may optimize away the temporary instances of inner classes, but for stateless lambda expressions there are no temporary instances in the first place. – Holger Sep 02 '15 at 18:48
  • 1
    @Holger - nice answers – ZhongYu Sep 02 '15 at 20:23

1 Answers1

8

How does the second example (without lambdas) work? A new object (Predicate and Consumer) created every time i call the code, how much can java jit compiler optimalize a lambda expression? For a better performace should i declare all lambdas as a variable and always only pass a reference?

The job of the JIT is to make it so you don't have to worry about any of this. For example, the JIT may (and, I believe, will) make it so that s -> s.length == 10 automatically becomes a static constant, so it's reused across the entire class, not just that instance. It might even get reused across other classes that use the same lambda somewhere else.

The entire point of lambdas is that you should just write the simplest code that makes sense and the JIT has the freedom to do whatever it needs to to make that efficient. For example, that's why lambdas are generated using invokedynamic instead of being simply desugared to anonymous inner classes in the first place.

Write the straightforward code; trust the JIT to do the right thing. That's what it's there for. That said, if you want the overwhelming, complicated nitty-gritty details, this is almost certainly the best source.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 3
    Since `s -> s.length() == 10` is invariant, it always becomes a constant, without the need for the JIT’s help. [This](http://stackoverflow.com/q/27524445/2711488) even applies to the interpreted mode… – Holger Sep 02 '15 at 18:53
  • I'm reading through the linked doc specifying the implementation. AFAICT, `s -> s.length() == 10` is lowered to a private static method, but I am still under the impression that the JIT implements the lambda metafactory that decides how to convert that into the interface instance? – Louis Wasserman Sep 02 '15 at 18:54
  • 2
    No, the [`LambdaMetafactory`](http://docs.oracle.com/javase/8/docs/api/?java/lang/invoke/LambdaMetafactory.html) is an ordinary Java class which generates bytecode using the well-known ASM library. You can even step through it using a Java debugger when a lambda expression is instantiated the first time. But due to how the `invokedynamic` instruction works, subsequent executions will use the result of that first invocation which is either a handle to a constant or a factory method/ constructor invocation. – Holger Sep 02 '15 at 18:59
  • @Holger, wait, I'm not certain again. I thought the `invokedynamic` gave you a handle to a constant method, sure -- but I thought that constant handle was a handle to the method that returned the lambda object, not that the lambda was itself guaranteed to be a constant? – Louis Wasserman Sep 02 '15 at 19:13
  • 2
    It depends on what the meta factory returns. In case of a stateless lambda, it will return a [handle which wraps a constant](http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.html#constant-java.lang.Class-java.lang.Object-) whose evaluation merely means “return the constant”. In case of capturing lambda, the handle will indeed represent executable code which in turn produces the lambda instance, i.e. a factory method or constructor invocation. – Holger Sep 02 '15 at 19:18