Does Java compiler optimize stream filtering?

Mikhail Kopylov :

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(...)
Jacob G. :

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.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=469156&siteId=1