下面用Java 8编译的代码可以正常工作,但不能用Java 9工作.不确定STREAMS执行过程中发生了什么变化.

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;
    }
}

代码中的print方法在使用8执行时打印消息,但在使用9执行时不打印消息.

PS:我知道可以通过将可选逻辑更改为stream().flatmap(...)来简化流代码,但这不是重点.

推荐答案

您依赖于通过map()执行的side-effects操作,文档(especially if in your real code you're doing something more important than printing 101 on the console)不鼓励这样做.

以下是Stream API documentation强中的一句话:

流操作的行为参数的副作用包括 一般,不鼓励,因为它们往往会导致无意中的违规行为 无状态要求以及其他线程安全 危险.

如果行为参数确实有副作用,除非明确 如上所述,有no guarantees项涉及:

  • 这些副作用对其他线索的可见性;
  • 在同一个线程中对同一流管道中的"同一"元素执行不同的操作;以及
  • that behavioral parameters are always invoked,因为流实现可以自由地从 流水线,如果它能证明它不会影响结果 计算.

副作用的消除也可能令人惊讶.除了终端操作forEach和行为参数may not always be executedforEachOrderedside-effects之外,当计算的流implementation can optimize away the execution of behavioral parameters without affecting the result时.

已添加重点

这意味着实现可以自由地将side-effects从它们不需要的地方删除,或者(and)不会影响流执行的结果.

这种优化的一个例子是count()运算的行为:

实现可以 Select 不执行流流水线 (顺序或并行)如果它能够计算 直接从流源开始计数.在这种情况下,没有消息来源 元素将被遍历,并且不会执行任何中间操作 已判断.".

因为如果流的源可以提供有关元素数量的信息,则Java 9 count将优化不会改变流中元素数量的操作.

另一个例子是省略peek()操作,根据文档"exists mainly to support debugging",它可以被优化,因为它不会影响reducecollect等终端操作产生的结果,也不会干扰forEach/forEachOrdered执行的结果操作.

您可以找到peek已被省略的情况的描述(whilst 102 of the other intermediate operations was skipped).


The bottom line:代码不应该依赖于行为,这是不能保证的.

Java相关问答推荐

伪类focus-in不适用于PFA中的选项卡

如何在Android上获取来电信息

为什么BasicComboBoxRenderer在文本不存在或文本为空的情况下设置两次文本?

如何打印本系列的第n项y=-(1)-(1+2)+(1+2+3)+(1+2+3+4)-(1+2+3+4+5)...Java中的(1+2+3+4...+n)

在Spring Boot中使用哪个Java类来存储创建时间戳?

按属性值从流中筛选出重复项

如何为JavaFX Spring Boot应用程序制作Windows/MacOS/Linux安装程序

如何在不删除Java中已有内容的情况下覆盖文件内容?

Java页面筛选器问题

buildDir:File!&#的getter解决方案是什么?39.被抛弃

无法使用Java PreparedStatement在SQLite中的日期之间获取结果

为什么这种递归会有这样的行为?

用于Java的Visual Studio代码完成不起作用

JavaFX,GridPane:在GridPane的列中生成元素将保持所有列的宽度

不能在 map 上移除折线

Kotlin-仅替换字符串中最后一个给定的字符串

如何通过gradle命令行从build.gradle获得Java targetCompatibility

在不带instanceof或switch的java中记录模式

OpenAPI Maven插件生成错误的Java接口名称

为什么 Random() 的行为不符合预期?