Functionally, both statements are equivalent. However, consider the following two blocks of code and their corresponding byte codes:
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
2: ldc #18
4: ldc #20
6: invokestatic #22
9: astore_1
10: aload_1
11: invokeinterface #28, 1
16: invokedynamic #35, 0
21: invokeinterface #36, 2
26: invokedynamic #42, 0
31: invokeinterface #36, 2
36: getstatic #43
39: invokedynamic #52, 0
44: invokeinterface #53, 2
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
2: ldc #18
4: ldc #20
6: invokestatic #22
9: astore_1
10: aload_1
11: invokeinterface #28, 1
16: invokedynamic #35, 0
21: invokeinterface #36, 2
26: getstatic #42
29: invokedynamic #51, 0
34: invokeinterface #52, 2
39: return
We see that in the second example one call is missing invokedynamicand invokeinterface(which makes sense since we omitted the call filter). I am sure that someone can help me with a static analysis of this bytecode (I can add detailed files if necessary), but the Java compiler explicitly treats a single call filteras a single call Predicate<String>, rather than splitting it into an operator &&, slightly reducing the bytecode.
source
share