The below code compiled with Java 8 works as expected but doesn't work with Java 9. Not sure what changed in the Streams execution.
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.lang.*;
public class TestingJavaStream {
public static void main(String[] args) {
Message message = new Message();
message.setName("Hello World!");
Stream<Message> messageStream = streamNonnulls(Collections.singleton(message))
.filter(not(Collection::isEmpty))
.findFirst()
.map(Collection::stream)
.orElseGet(Stream::empty);
System.out.println("Number of messages printed are "
+ messageStream
.map(TestingJavaStream::print)
.count());
}
public static class Message {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Message other = (Message) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Message [name=" + name + "]";
}
}
@SafeVarargs
public static <T> Stream<T> streamNonnulls(T... in) {
return stream(in).filter(Objects::nonNull);
}
@SafeVarargs
public static <T> Stream<T> stream(T... in) {
return Optional.ofNullable(in)
.filter(arr -> !(arr.length == 1 && arr[0] == null))
.map(Stream::of)
.orElseGet(Stream::empty);
}
public static <T> Predicate<T> not(Predicate<T> p) {
return (T x) -> !p.test(x);
}
public static Message print(Message someValue) {
System.out.println("Message is :: "+someValue.toString());
return someValue;
}
}
The print method in the code prints the message when executed with 8 but doesn't when executed with 9.
PS: I understand the stream code can be simplified by changing the Optional logic to stream().flatmap(...) but that's beside the point.
You're relying on the side-effects performed via
map()operation, which is discouraged by the documentation (especially if in your real code you're doing something more important than printing"hello"on the console).Here's a quote from the Stream API documentation:
Emphases added
That means that implementations are free to elide the side-effects from the places where they are not expected or(and) would not affect the result of the stream execution.
An example of such optimization is the behavior of the
count()operation:Since Java 9
countwould optimize away operations that doesn't change the number of elements in the stream in case if the source of the stream can provide the information about the number of elements.Another example is eliding of the
peek()operation, which according to the documentation "exists mainly to support debugging" and can be optimized away since it doesn't supposed to contribute to the result produced by the terminal operations likereduceorcollect, or interfere with the resulting action performed byforEach/forEachOrdered.Here you can find a description of the case where
peekhas been elided (whilst none of the other intermediate operations was skipped).The bottom line: the code should not depend on the behavior, which is not guaranteed.