Как именно работает функция Java .reduce с 3 параметрами? - PullRequest
1 голос
/ 05 марта 2020

В настоящее время я изучаю java.reduce(), и недавно я кое-что обнаружил, читая некоторые материалы и просматривая видео. Я понимаю, что есть 3 способа использовать это, но у меня есть проблемы с получением 3 параметра для работы. Я надеялся, что кто-то сможет лучше объяснить, что с ним происходит, и почему я не получаю результаты, которые я исключил (среднее для набора объектов MagicNumber). Вот пример того, что у меня есть:

Объект, назовем его MagicNumber. java

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

Случайный поток, содержащий маги c числа

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

Функция для возврата среднего числа волхвов c число

// 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
    )
}

Как следует из комментария, приведенное выше возвращает только сумму всех чисел волхвов c. Пока я чего-то ожидаю. Поэтому я не верю, что полностью понимаю назначение третьего параметра в функции reduce. Кроме того, когда я пишу что-то вроде следующего, используя 2 параметра: Функция для возврата среднего волхва c число

// 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. Может ли кто-нибудь объяснить как (А), почему эти две average функции не работают должным образом, и (Б), как изменить их, чтобы получить результат, который я ищу? Буду очень признателен за помощь!

Ответы [ 2 ]

4 голосов
/ 05 марта 2020

Вы неправильно понимаете 3-й аргумент reduce(). Этим аргументом является функция объединения, которая объединяет два промежуточных результата в один результат.

Теперь причина, по которой вы не можете вычислить среднее значение, заключается в том, что для вычисления среднего значения вам нужно уменьшить значение Stream на 2 значения - одно количество элементов, а другое их сумма. Получив эти два значения, вы можете разделить их.

Чтобы reduce() мог получить два значения, вы можете использовать некоторый класс, который содержит два значения, например 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();
}
1 голос
/ 06 марта 2020

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ - Я новичок в лямбдах и потоках в целом.

Я думаю Ответ Эрана лучше. Но я также добавлю свой ответ. Если необходимо, вот краткий учебник о потоках, связанных с этим вопросом.

Я использовал список выбранных целых чисел вместо ваших "магических чисел c". Это то же самое, потому что числа волхвов c в вашем примере просто целые числа. Таким образом, мы на самом деле просто вычисляем среднее целых чисел. Кроме того, это будет легко проверить и узнать с помощью списка, номера которого мы можем решить. Как только вы выясните лямбда-часть, вы можете заменить 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
...