10

Let's have a case:

x.stream().filter(X::isFlag).filter(this::isOtherFlag).reduce(...)

Does it differ from this one?

x.stream().filter(predicate(X::isFlag).and(this::isOtherFlag)).reduce(...)
9
  • 2
    You can check by running javap -c <class>, if piped to a file (javap -c StreamTest > out.txt), you can compare them easily Commented Jan 29, 2018 at 20:41
  • 3
    The Java "compiler" (as in javac, the program) does not optimize this. However, the library may indeed collapse compatible steps in a Stream, see the detailed description of the different types of operations and how they interact Commented Jan 29, 2018 at 20:41
  • You can always look at the source code to see how it keeps track of filters but nowhere in Javadoc it says that the implementation will stay the same or if it will optimize in any way. Commented Jan 29, 2018 at 20:41
  • I don't think they are different performance wise to begin with. Not until I see benchmark test results. Commented Jan 29, 2018 at 20:43
  • 1
    @the8472 the saved object allocation has to be compensated within the stream implementation one way or another. In either case, there must be an encapsulated piece of code evaluating both predicates. It would be different when using a single .filter(x -> x.isFlag() && isOtherFlag(x)), however. Then, you have a slight headstart on the JIT side… Commented Jan 30, 2018 at 14:41

1 Answer 1

7

Functionally, the two statements are equivalent. However, consider the two following blocks of code and their respective bytecodes:

public static void main(String[] args) {
    List<String> list = List.of("Seven", "Eight", "Nine");

    list.stream().filter(s -> s.length() >= 5)
                 .filter(s -> s.contains("n"))
                 .forEach(System.out::println);
}

public static void main(java.lang.String[]);
Code:
   0: ldc           #16                 // String Seven
   2: ldc           #18                 // String Eight
   4: ldc           #20                 // String Nine
   6: invokestatic  #22                 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
   9: astore_1
  10: aload_1
  11: invokeinterface #28,  1           // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
  16: invokedynamic #35,  0             // InvokeDynamic #0:test:()Ljava/util/function/Predicate;
  21: invokeinterface #36,  2           // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
  26: invokedynamic #42,  0             // InvokeDynamic #1:test:()Ljava/util/function/Predicate;
  31: invokeinterface #36,  2           // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
  36: getstatic     #43                 // Field java/lang/System.out:Ljava/io/PrintStream;
  39: invokedynamic #52,  0             // InvokeDynamic #2:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
  44: invokeinterface #53,  2           // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
  49: return

-

public static void main(String[] args) {
    List<String> list = List.of("Seven", "Eight", "Nine");

    list.stream().filter(s -> s.length() >= 5 && s.contains("n"))
                 .forEach(System.out::println);
}

public static void main(java.lang.String[]);
Code:
   0: ldc           #16                 // String Seven
   2: ldc           #18                 // String Eight
   4: ldc           #20                 // String Nine
   6: invokestatic  #22                 // InterfaceMethod java/util/List.of:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/List;
   9: astore_1
  10: aload_1
  11: invokeinterface #28,  1           // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
  16: invokedynamic #35,  0             // InvokeDynamic #0:test:()Ljava/util/function/Predicate;
  21: invokeinterface #36,  2           // InterfaceMethod java/util/stream/Stream.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
  26: getstatic     #42                 // Field java/lang/System.out:Ljava/io/PrintStream;
  29: invokedynamic #51,  0             // InvokeDynamic #1:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
  34: invokeinterface #52,  2           // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
  39: return

We can see that, in the second example, one call to invokedynamic and invokeinterface are missing (which makes sense as we omitted a call to filter). I'm sure someone could assist with me with the static analysis of this bytecode (I can post verbose files if needed), but the Java compiler clearly treats the single call to filter as a single Predicate<String> rather than splitting it at the operator &&, shortening the bytecode slightly.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.