Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用Java8流取代传统的newForLoop_Java_Foreach_Lambda_Java 8_Java Stream - Fatal编程技术网

用Java8流取代传统的newForLoop

用Java8流取代传统的newForLoop,java,foreach,lambda,java-8,java-stream,Java,Foreach,Lambda,Java 8,Java Stream,最后,我从Java6到Java8有了一个相对较大的飞跃,我阅读了大量Java8StreamsAPI。不幸的是,几乎所有被问到的例子几乎都接近于我想弄明白的方法,但还不够接近 我所拥有的是 final List<Function<? super Double, Double>> myList = generateList(); final double myVal = calculate(10); private double calculate(double val)

最后,我从Java6到Java8有了一个相对较大的飞跃,我阅读了大量Java8StreamsAPI。不幸的是,几乎所有被问到的例子几乎都接近于我想弄明白的方法,但还不够接近

我所拥有的是

final List<Function<? super Double, Double>> myList = generateList();
final double myVal = calculate(10);

private double calculate(double val) {
    for (Function<? super Double, Double> function : this.myList) {
        val += function.apply(val);
    }
    return val;
}

final List是的,您可以通过执行减少操作来使用流解决方案:

缩减将每个元素合并为一个值。有3种风格的
reduce()
方法-这里使用的方法就可以做到这一点


一些测试代码:

static Function<? super Double, Double> a = (d) -> d + 4;
static Function<? super Double, Double> b = (d) -> d * 2;
static Function<? super Double, Double> c = (d) -> d - 3;
static List<Function<? super Double, Double>> myList = Arrays.asList(a, b, c);

static double calculate(double val) {
    return myList.stream().reduce(val, (d, f) -> d + f.apply(d), (a, b) -> a + b);
}

public static void main(String[] args) {
    System.out.println(calculate(10));
}

这个特定的示例对于Java8流来说是非常有问题的。它们是为顺序不重要的操作而设计的

函数应用程序不是关联的。为了解释,让我们举一个更简单的例子,在这个例子中,我们想取一个数字,并用一个数字表把它除以:< /P>
static List<Double> dividers = Arrays.asList( 3.5, 7.0, 0.5, 19.0 );

public double divideByList( double a ) {
    for ( Double d : dividers ) {
        a /= d;
    }
    return a;
}
算术是简单的-除法是左结合的,这等于

a ÷ ( 3.5 × 7.0 × 0.5 × 19.0)
不是

a ÷ ( 3.5 ÷ 7.0 ÷ 0.5 ÷ 19.0 )
不是

( a ÷ 3.5 ÷ 7.0 ) ÷ ( 0.5 ÷ 19.0 )
基于reduce/collector的流操作要求“reduce”操作保持关联。这是因为他们希望允许操作并行化,这样一些线程将执行一些操作,然后可以组合结果。现在,如果我们的运算符是乘法而不是除法,这不会是个问题,因为

a × 3.5 × 7.0 × 0.5 × 19.0

(a × 3.5 × 7.0 ) × (0.5 × 19)
这意味着一个线程可以执行
a×3.5×7.0
,另一个线程可以执行
0.5×19.0
操作,然后可以将结果相乘,得到与顺序计算相同的结果。但对于部门来说,这是行不通的

函数应用程序也是非关联的,就像除法一样。也就是说,如果您有函数
f
g
h
,并且您运行顺序计算,您将得到:

result = val + f(val) + g(val + f(val)) + h(val + f(val) + g(val + f(val)))
现在,如果您有两个中间线程,一个应用
f
g
,另一个应用
h
,并且您希望合并结果-首先无法将正确的值放入
h


正如@Bohemian所建议的,您可能会尝试使用类似于
Stream.reduce
的方法。但是,报告警告您不要这样做:

对于像
+
这样的操作,标识为0。对于
*
,标识为1。因此,使用
val
作为
标识是违反文档的。第二个条件问题更大

尽管非并行流的当前实现
没有使用合并器部分,这使得这两个条件都不需要,但这是未记录的,未来的实现或不同的JRE实现可能会决定创建中间结果,并使用合并器将其合并,可能是为了提高性能或出于任何其他考虑

因此,尽管存在诱惑,但不应使用
Stream.reduce
来尝试模仿原始的顺序处理


有一种方法可以做到这一点,它实际上不会破坏文档。它涉及到保留一个保存结果的可变对象(它必须是一个对象,以便在仍然可变的情况下它是有效的最终对象),并使用,如果流是有序的,这将确保操作将按照它们在流中出现的顺序执行。列表的流具有定义的顺序。即使使用
myList.stream().parallel()
,也可以使用此功能

就我个人而言,我发现您原来的循环比这个更可读,所以在这里使用流确实没有什么好处


根据@Tagir Valeev的评论,计划在未来的Java版本中进行操作。当这种情况发生时,它可能看起来更加优雅。

您可以使用流API从函数列表中组合函数

static List<Function<? super Double, Double>> myList
    = Arrays.asList(d -> d + 4, d -> d * 2, d -> d - 3);

static Function<Double, Double> total=myList.stream()
    .map(f -> (Function<Double, Double>) d -> d + f.apply(d))
    .reduce(Function::andThen).orElse(Function.identity());

static double calculate(double val) {
    return total.apply(val);
}

public static void main(String[] args) {
    System.out.println(calculate(10));
}

静态列表我不认为它做同样的事情。最初,对于三个函数f,g,h,会给出f(val)+g(f(val))+h(f(val)+g(f(val)),而这个减缩器允许将部分结果与
+
结合起来,因此可以得到f(val)+g(val)+h(val)或类似的结果。这里的组合器不符合文档中的约束。我只是进行了逻辑测试,这就是代码真正的功能。10+(10+4)=24然后24+(24*2)=72然后72+(72-3)=141最终结果仍然相同。现在,我真的很好奇在这种情况下使用流与传统方法相比的性能成本。实际上,这是(希望)即将到来的工作。这里的操作是非关联的,所以reduce只会并行产生不正确的结果。就是这样,不是吗?合并器从未被调用的事实是没有记录的,这是一个不应该依赖的实现细节,这是故意的,应该避免对同一流的并行和非并行版本产生不相同结果的任何使用。如果代码依赖于从未被调用的合并器,它至少应该提供
(x,y)->{throw new AssertionError();}
作为组合器,而不是默默地生成错误值的东西@gabizou:您的性能测试缺少任何预热阶段,因此您正在测量流框架的类加载和初始化时间。不,不是真的。
Stream
操作对于像您正在做的那样固有的顺序操作并不真正起作用。我理解,我只是希望流最终会有相同的性能。在大范围内,很可能对于大型函数集,这可能会以奇偶性结束,但对于我所做的,这是不可能的。当然,随着新Java版本的开发,我们可能会看到优化和增强
(a × 3.5 × 7.0 ) × (0.5 × 19)
result = val + f(val) + g(val + f(val)) + h(val + f(val) + g(val + f(val)))
<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)
combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
public static double streamedCalculate(double val) {
    class MutableDouble {
        double currVal;
        MutableDouble(double initVal) {
            currVal = initVal;
        }
    }

    final MutableDouble accumulator = new MutableDouble(val);

    myList.stream().forEachOrdered((x) -> accumulator.currVal += x.apply(accumulator.currVal));
    return accumulator.currVal;
}
static List<Function<? super Double, Double>> myList
    = Arrays.asList(d -> d + 4, d -> d * 2, d -> d - 3);

static Function<Double, Double> total=myList.stream()
    .map(f -> (Function<Double, Double>) d -> d + f.apply(d))
    .reduce(Function::andThen).orElse(Function.identity());

static double calculate(double val) {
    return total.apply(val);
}

public static void main(String[] args) {
    System.out.println(calculate(10));
}