具有 3 个参数的 Java.reduce 函数究竟是如何工作的?

How exactly does the Java.reduce function with 3 parameters work?

我目前正在学习 java.reduce(),我最近在阅读一些 material 和浏览视频时遇到了一些事情。我知道有 3 种方法可以使用它,但是,我在使用 3 参数之一时遇到了问题。我希望有人能更好地解释它发生了什么,以及为什么我没有得到我例外的结果(一组 MagicNumber 对象的平均值)。这是我目前所拥有的示例:

一个对象,我们称它为MagicNumber.java

public class MagicNumber{
    Random randomValueGenerator = new Random();
    int number;
    public MagicNumber(){
        this.number = randomValueGenerator.nextInt();
    }
}

包含幻数的随机流

public static Stream<MagicNumber> getNStream(){
    List<MagicNumber> magic = new ArrayList<MagicNumber>();
    for(int i = 0; i < 100; i++){
        magic.add(new MagicNumber());
    }
    return magic.stream();
}

return平均幻数

的函数
// This only returns the sum of the magic numbers and not the average
public static double average(){
    Stream<MagicNumbers> magicNumberStream = getNStream();
    return magicNumberStream.reduce(
        0.0, // initial value
        (a,b) -> a + (double)b.number,
        (a,b) -> (a + b)/100
    )
}

正如评论所暗示的那样,以上只是 return 所有幻数的总和。虽然我期待什么。所以,我不相信我完全理解 reduce 函数中第三个参数的用途。另外,当我使用 2 个参数编写如下内容时: return 平均幻数

的函数
// This throws an error due to the return type
public static double average(){
    Stream<MagicNumbers> magicNumberStream = getNStream();
    return magicNumberStream.reduce(
        0.0, // initial value
        (a,b) -> (double)(a.number + b.number)/100
    )
}

我收到错误 Bad return type in lambda expression: double cannot be converted to MagicNumber。有人可以解释 (A) 为什么这两个 average 函数没有按预期工作,以及 (B) 如何修改它们以获得我正在寻找的结果?非常感谢您的帮助!

您误解了 reduce() 的第三个参数。该参数是组合器函数,它将两个中间结果组合成一个结果。

现在,您无法计算平均值的原因是为了计算平均值,您必须将 Stream 减少为 2 个值 - 一个是元素的数量,另一个是它们的总和.一旦你得到这两个值,你就可以将它们相除。

为了让 reduce() 产生两个值,您可以使用一些包含两个值的 class,例如 SimpleEntry.

public static double average(){
    Stream<MagicNumber> magicNumberStream = getNStream();
    Map.Entry<Integer,Double> sums = 
        magicNumberStream.reduce(
            new SimpleEntry<Integer,Double>(0,0.0), // initial value (number of elements, sum)
            (a,b) -> new SimpleEntry<>(a.getKey()+1,a.getValue()+b.number),
            (a,b) -> new SimpleEntry<>(a.getKey()+b.getKey(),a.getValue()+b.getValue()));
    return sums.getValue()/sums.getKey();
}

免责声明 - 一般来说,我是 lambda 和流的初学者。

我认为 is better. But, i'll add my answer also. If needed, here is a short tutorial 在 Streams 上与这个问题相关。

我使用了一个选定整数列表而不是你的"magic numbers"。这是相同的,因为幻数在您的示例中只是整数。所以我们实际上只是在计算整数的平均值。此外,使用我们可以决定其数量的列表来测试和学习将很容易。弄清楚 lambda 部分后,您可以将 List<Integer> 替换为 List<MagicNumber> 并相应地更新代码。

虽然不需要,但我会使用并行流来回答你的问题。代码解释在评论里

import java.util.Arrays;
import java.util.List;

public class Temp {
    public static void main(String [] args){
        //For example, consider a list of a *series* of numbers in increasing order.
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        int n = numbers.get(numbers.size()-1);//n = 6 is the number of numbers in list.

        double expectedSum = (n * (n + 1))/2;//By mathematical formula for increasing series.

        Double sum = numbers
                //Take a stream of numbers - it could be integers, "magic numbers" or any other number.
                .stream()
                //Split the stream into mini streams and calculate sum of each of mini stream.
                .parallel()
                //Reduce all the numbers in a stream to their sum.
                .reduce(
                        //start with a partial sum of 0.
                0.0,
                //For a stream, calculate the sum of all the numbers.
                (partialSum, nextNumber) -> partialSum + (double) nextNumber,
                //Add the sums of each mini stream.
                (onePartialSum, anotherPartialSum) -> (onePartialSum + anotherPartialSum)
        );

        System.out.println("Sum : expected value = " + expectedSum + ", actual value = " + sum);

        double expectedAverage = expectedSum/numbers.size();
        double average = sum/numbers.size();

        System.out.println("Average : expected value = " + expectedAverage + ", actual value = " + average);

    }
}

输出:

Sum : expected value = 21.0, actual value = 21.0
Average : expected value = 3.5, actual value = 3.5

这里有一些我想根据具有 3 个参数的 reduce 方法如何工作的问题讨论的要点? 1.if 我们不使用并行 Stream 方法然后简单地减少方法使用标识和累加器, 下面是相同的演示,

    int length=se.reduce(0,//identity
    //below is the Accumulator that is Bifunctional parameter
    (i,s)->{
        System.out.println("Initial Value= "+i+" with Stream Object="+s+" Addition is ="+(i+s.length()));//extra work to understand the data        
        return i+s.length();},
    //below is the BinaryOperator method which never gets executed in single pipe Stream**strong text**,    
    (a,b)->{
            System.out.println("First Value that passed to combiner = "+a+" Second value ="+b);//never gets called in single stream
        return a+b;});
    System.out.println(length); 

输出

Initial Value= 0 with Stream Object=I Addition would be =1
Initial Value= 1 with Stream Object=am Addition would be =3
Initial Value= 3 with Stream Object=The Addition would be =6
Initial Value= 6 with Stream Object=King Addition would be =10
Initial Value= 10 with Stream Object=Of Addition would be =12
Initial Value= 12 with Stream Object=Java Addition would be =16
16

在我们应用并行流的第二种情况下,Combiner 将被执行,

public static int z=0;//need to put outside of public static void main(String[] args);

List<String> ll=List.of("I","am","very","Brave");
    ll.parallelStream().reduce(0,(i,s)->{
        System.out.println("Initial Value= "+i+" with Stream Object="+s+" With Stream Number= "+z++);
        return i+s.length();},(a,b)->{
            System.out.println("First Value that passed to combiner = "+a+" Second value ="+b);
        return a+b;});
System.out.println(Length);

在 运行 之后,我们得到以下输出,

Initial Value= 0 with Stream Object=very With Stream Number= 0
Initial Value= 0 with Stream Object=am With Stream Number= 1
Initial Value= 0 with Stream Object=I With Stream Number= 2
Initial Value= 0 with Stream Object=Brave With Stream Number= 3
First Value that passed to combiner = 1 Second value =2
First Value that passed to combiner = 4 Second value =5
First Value that passed to combiner = 3 Second value =9
12

这清楚地表明身份每次只是传递 0 值,因此每个流都是不同的,需要组合器来组合结果, 谢谢!