Java функциональное программирование для нескольких функций с одним потоком данных - PullRequest
0 голосов
/ 16 июня 2020

Существует список таких объектов, как: -

ID     Employee    IN_COUNT    OUT_COUNT     Date
1        ABC          5           7        2020-06-11 
2        ABC          12          5        2020-06-12 
3        ABC          9           6        2020-06-13

Это данные сотрудника за три даты, которые я получаю из запроса в объекте List.
Не хочу всего число IN_COUNT и OUT_COUNT между тремя датами. Этого можно достичь, выполнив первую итерацию потока только для IN_COUNT и вызвав sum(), а затем во второй итерации можно суммировать только OUT_COUNT данных. Но я не хочу повторять список дважды.
Как это возможно при функциональном программировании с использованием потока или любого другого варианта.

Ответы [ 2 ]

0 голосов
/ 16 июня 2020

Для этого можно использовать reduce:

public class Counts{

    private int inCount;
    private int outCount;

    //constructor, getters, setters

}

public static void main(String[] args){

    List<Counts> list = new ArrayList<>();
    list.add(new Counts(5, 7));
    list.add(new Counts(12, 5));
    list.add(new Counts(9, 6));

    Counts total = list.stream().reduce(

                       //it's start point, like sum = 0
                       //you need this if you don't want to modify objects from list
                       new Counts(0,0), 

                       (sum, e) -> {

                           sum.setInCount( sum.getInCount() + e.getInCount() );
                           sum.setOutCount( sum.getOutCount() + e.getOutCount() );

                           return sum;

                       }

                   );

    System.out.println(total.getInCount() + " - " + total.getOutCount());

}
0 голосов
/ 16 июня 2020

То, что вы пытаетесь сделать, называется операцией «сворачивания» в функциональном программировании. Потоки Java называют это «сокращение» и «сумма», «счет» и т. Д. c. просто специализированные складки / складки. Вам просто нужно предоставить двоичную функцию накопления. Я предполагаю Java геттеры и сеттеры стиля Bean и конструктор всех аргументов. Мы просто игнорируем другие поля объекта в нашем накоплении:

List<MyObj> data = fetchData();
Date d = new Date();
MyObj res = data.stream()
    .reduce((a, b) -> {
        return new MyObj(0, a.getEmployee(),
            a.getInCount() + b.getInCount(), // Accumulate IN_COUNT
            a.getOutCount() + b.getOutCount(), // Accumulate OUT_COUNT
            d);
     })
    .orElseThrow();

Это упрощено и предполагает, что у вас есть только один сотрудник в списке, но вы можете использовать стандартные операции с потоком для разделения и группировки вашего потока (groupBy).

Если вы не хотите или не можете создавать MyObj, вы можете использовать другой тип в качестве аккумулятора. Я буду использовать Map.entry, потому что Java не имеет типа Pair / Tuple:

Map.Entry<Integer, Integer> res = l.stream().reduce(
        Map.entry(0, 0), // Identity
        (sum, x) -> Map.entry(sum.getKey() + x.getInCount(), sum.getValue() + x.getOutCount()), // accumulate
        (s1, s2) -> Map.entry(s1.getKey() + s2.getKey(), s1.getValue() + s2.getValue()) // combine
    );

Что здесь происходит? Теперь у нас есть функция уменьшения Pair accum, MyObj next -> Pair. «Идентичность» - это наше начальное значение, функция накопителя добавляет следующий MyObj к текущему результату, а последняя функция используется только для объединения промежуточных результатов (например, если выполняется параллельно).

Слишком сложно? Мы можем разделить этапы извлечения интересных свойств и их накопления:

Map.Entry<Integer, Integer> res = l.stream()
    .map(x -> Map.entry(x.getInCount(), x.getOutCount()))
    .reduce((x, y) -> Map.entry(x.getKey() + y.getKey(), x.getValue() + y.getValue()))
    .orElseGet(() -> Map.entry(0, 0));
...