5

Let's say we have three methods like these. They all do the same, but how different they are in terms of memory allocation and efficiency? Method one will create instances of Function during each call, but will the second method do the same? Should one always use the third version or either version is safe and the JIT compiler will take care of the memory optimization?

class Test {
    Map<String, Set<String>> mapOfSets = new HashMap<>();
    static final Function<String, Set<String>> FUNCTION = s -> new HashSet<>();

    void method1(String key, String value) {
        Function<String, Set<String>> func = s -> new HashSet<>();
        mapOfSets.computeIfAbsent(key, func).add(value);
    }

    void method2(String key, String value) {
        mapOfSets.computeIfAbsent(key, s -> new HashSet<>()).add(value);
    }

    void method3(String key, String value) {
        mapOfSets.computeIfAbsent(key, FUNCTION).add(value);
    }
}
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
andbi
  • 4,426
  • 5
  • 45
  • 70
  • 2
    There is a common problem in all questions about the efficiency of anything in Java: Java is a specification, the efficiency is determined by the implementation of that specification. The characteristics of a piece of Java code can be quite different from one implementation to another. – sprinter Dec 22 '18 at 03:55
  • In addition to @sprinter: Also there are implementations which not even implement all parts of the specification and nevertheless often are called `Java`. `Andoid` for example. – Axel Richter Dec 22 '18 at 05:08

2 Answers2

7

As comments have mentioned, this can change depending on what Java implementation you use.

For the hotspot jvm (the normal/default java implementation) you can expect all 3 options to be identical in terms of performance. You may think at first that 3 might be better since 1 and 2 might need to create a new Function object each time they are called. However, your assumption that a new object needs to be created each time for method1 (and 2) is false since a non-capturing lambda will be cached at the call site, so the same instance will be reused for each call just like with method3 and you would not see any difference.

this answer is and excellent overview of the topic goes into more detail

So you can use any of the three without worry in this case so use whichever is the most clear for your situation.

puhlen
  • 8,400
  • 1
  • 16
  • 31
  • This should be the correct answer. Non-capturing lambdas will be cached. "there’s no imaginable reason to do it differently" – gary Jan 31 '21 at 09:28
5

There are two ways to answer this:

The Java Language Specification section that describes the evaluation of a lambda expression is written to give compilers / implementations flexibility in whether or not a new object is created. Your question boils down to whether or not the lambda objects are reused ... in the three versions of the code. According to the JLS, the answer is implementation dependent. The only way to be 100% sure is to dump out the native code produced by the JIT compiler1. However according to @Holger's answers to other questions2, current OpenJDK-based Java runtimes do cache and reuse non-capturing lambdas.

Given that the behavior and therefore the performance will depend on the Java implementation, it is debatable whether one should pick one form over the other:

  • On the one hand, the third form may be faster.
  • On the other hand, the second and (maybe) first forms are more readable.
  • Should one write applications to be a bit faster at the expense of readability? (Q1)
  • Should one do that, in the face of an expectation that the next version of the compiler may make the hand optimization redundant? (Q2)

This is a matter of opinion, but my opinion is that the answers to Q1 & Q2 is "No" under most circumstances. Leave it to the JIT compiler to deal with optimization unless profiling tells you that the optimization is significant to your application's overall performance, and your application's overall performance really matters.


1 - See How to see JIT-compiled code in JVM?.
2 - See https://stackoverflow.com/a/27524543/139985 and https://stackoverflow.com/a/23991339/139985.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    This is a great answer to a great question. I wish I knew how to <>, then interpret which form is used. Ideally: Can you update this answer with a *single* sample? Example: OpenJDK 11 on x86-64 Linux. Or can you give me a hint? I think it would be great to update this answer (or create a new answer) with some concrete results from well-known JIT compilers. – kevinarpe Aug 18 '22 at 05:18
  • 1. I added a link in a footnote. 2. Feel free to do the work and add an answer. I don't think it is worth the effort. – Stephen C Aug 18 '22 at 05:32