如何将两个java8流操作 - 一个终端和一个懒惰 - 组合到一个操作中?

bis*_*rot 0 java functional-programming lazy-evaluation java-8 java-stream

我正在做一些Java 8流的"代数",也就是说,我正在尝试编写一个简单的操作Op,它将两个流作为输入并产生另一个流.

所以我有这个简单的代码,其目的是在一系列数字中打印secund最高值:

import java.util.Arrays;
import java.util.stream.IntStream;

public class SecundHighestValue {

    public static void main(String[] args) {

        //setting the input parameters
        int [] numbers = {1, 2, 3, 4, 3, 4, 2, 1};

        IntStream S1 = Arrays.stream(numbers);
        IntStream S2 = Arrays.stream(new int[] {Arrays.stream(numbers).max().getAsInt()} );

        // setting the operation
        IntStream S3 = S1.filter(x-> x != S2.toArray()[0]); // doesn't work

        /*** does work  ***
        int  maxNumber = S2.toArray()[0];
        IntStream S3 = S1.filter(x-> x != maxNumber);
        */

        // accessing the operation's result stream S3
        int secundMaxNumber = S3.max().getAsInt();
        System.out.println("the secund highest value in the serie " +
                    Arrays.toString(numbers) + " is " + secundMaxNumber);   
    }
}
Run Code Online (Sandbox Code Playgroud)

除非我以这种方式拆分单行操作,否则此程序将无法运行:

    int  maxNumber = S2.toArray()[0];
    IntStream S3 = S1.filter(x-> x != maxNumber);
Run Code Online (Sandbox Code Playgroud)

将操作保持在一行将引发此异常:

线程"main"中的异常java.lang.IllegalStateException:stream已被操作或关闭...

我知道它与filter()方法固有的懒惰有关.该API说明:

流操作分为中间(流生成)操作和终端(生成价值或副作用)操作.中间操作总是很懒惰.

实际上,堆栈跟踪显示在我尝试在下一行中访问其结果之前,操作不会执行.

这种行为在java8中是否有缺陷?这是一个错误吗?最重要的是,如何将操作保持在一行并使其工作?

Hol*_*ger 6

如果可以通过源进行流式传输并且不是很昂贵,就像使用数组一样,您可能只需要流式传输两次,就像在azro的答案中一样:

int maxNumber = Arrays.stream(numbers).max().getAsInt();
int secondMaxNumber = Arrays.stream(numbers).filter(x-> x != maxNumber).max().getAsInt();
Run Code Online (Sandbox Code Playgroud)

如果流式传输两次不可能或不昂贵,您需要一个自定义收集器来有效地获得第二大值,即只保留必要的两个值.例如

final class SecondMax {
    long max=Long.MIN_VALUE, semi=max;

    void add(int next) {
        if(next>semi) {
            if(next>max) {
                semi=max;
                max=next;
            }
            else if(next<max) {
                semi=next;
            }
        }
    }
    void merge(SecondMax other) {
        if(other.max>Long.MIN_VALUE) {
            add((int)other.max);
            if(other.semi>Long.MIN_VALUE) add((int)other.semi);
        }
    }
    OptionalInt get() {
        return semi>Long.MIN_VALUE? OptionalInt.of((int)semi): OptionalInt.empty();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此帮助程序,您可以在单个流操作中获取值:

OptionalInt secondMax = Arrays.stream(array)
  .collect(SecondMax::new, SecondMax::add, SecondMax::merge).get();
Run Code Online (Sandbox Code Playgroud)