Карта с промежуточной суммой в Java 8 - PullRequest
7 голосов
/ 19 марта 2019

Если у меня есть коллекция:

List<Long> numbers = asList(2, 2, 4, 5);

Как я могу отобразить / обработать их для создания промежуточного итога.Чтобы создать что-то вроде:

List<Long> runningTotals = asList(2, 4, 8, 13);

Еще лучше, как я могу создать список чего-либо (например, кортеж), чтобы я мог сохранить оригиналы:

((2 -> 2), (2 -> 4), (4 -> 8), (5 -> 13));

Ответы [ 5 ]

10 голосов
/ 19 марта 2019

Вам не нужна Java 8 для этого.На самом деле, эта проблема плохо подходит для потоков, потому что вычисление выполняется с учетом состояния, поскольку оно зависит от суммы предыдущих элементов, поэтому вы не получаете преимущества от таких вещей, как распараллеливание.

Выможет также просто использовать простой старый цикл:

ListIterator<Long> it = list.listIterator();
Long previous = it.next();  // Assuming the list isn't empty.
while (it.hasNext()) {
  it.set(previous += it.next());
}

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

AbstractList<Long> something = new AbstractList<Long>() {
  @Override public int size() { return list.size(); }
  @Override public Long get(int i) {
    if (i > 0) {
      return list.get(i) - list.get(i - 1);
    } else {
      return list.get(i);
    }
  }
};
5 голосов
/ 19 марта 2019

Вы можете использовать Arrays#parallelPrefix для достижения вашей цели:

List<Long> numbers = Arrays.asList(2L, 2L, 4L, 5L);
long[] copiedArray = numbers.stream().mapToLong(Long::longValue).toArray();

Arrays.parallelPrefix(copiedArray, Long::sum);

System.out.println(IntStream.range(0, numbers.size())
        .mapToObj(i -> "(" + numbers.get(i) + " -> " + copiedArray[i] + ")")
        .collect(Collectors.joining(", ", "[", "]")));

Выход:

[(2 -> 2), (2 -> 4), (4 -> 8), (5 -> 13)]
4 голосов
/ 19 марта 2019

Обновление : Как отметил Хольгер в комментариях, использование Stream.reduce() для этой цели не является правильным. См. Сокращение и Изменчивое сокращение или Потоки Java 8 - сбор и уменьшение для получения дополнительной информации.

Вместо этого вы можете использовать Java Stream.collect() для генерации списка с суммами:

List<Long> numbers = Arrays.asList(2L, 2L, 4L, 5L);
List<Pair> results = numbers.stream()
        .collect(ArrayList::new, (sums, number) -> {
            if (sums.isEmpty()) {
                sums.add(new Pair(number, number));
            } else {
                sums.add(new Pair(number, number + sums.get(sums.size() - 1).getSum()));
            }
        }, (sums1, sums2) -> {
            if (!sums1.isEmpty()) {
                long sum = sums1.get(sums1.size() - 1).getSum();
                sums2.forEach(p -> p.setSum(p.getSum() + sum));
            }
            sums1.addAll(sums2);
        });

Это объединяет все числа и создает пару для каждого числа с добавлением к предыдущей сумме. Он использует следующий класс Pair в качестве помощника:

public class Pair {
    private long number;
    private long sum;

    public Pair(long number, long sum) {
        this.number = number;
        this.sum = sum;
    }

    public long getNumber() {
        return number;
    }

    public void setSum(long sum) {
        this.sum = sum;
    }

    public long getSum() {
        return sum;
    }
}

Вы можете легко изменить этот вспомогательный класс, если хотите добавить больше информации.

Результат в конце:

[
    Pair{number=2, sum=2}, 
    Pair{number=2, sum=4}, 
    Pair{number=4, sum=8}, 
    Pair{number=5, sum=13}
]
0 голосов
/ 19 марта 2019

Я просто использовал функцию forEach в Java 8. Я инициализировал ввод типа Long. Я создал временный ArrayList (runningSum), который просто хранит текущие суммы, значение которых инициализируется в 0. (с индексом 1). ValuePair создает число и его текущую сумму для этой позиции, и оно сохраняется в результате (список) и отображается. Надеюсь, это поможет

package net.javapedia;

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

class ValuePairs {
    public ValuePairs(Long n, Long s) {
        number = n;
        sum = s;
    }

    Long number;
    Long sum;

    @Override
    public String toString() {

        return new StringBuilder("(").append(this.number).append(" -> ").append(this.sum).append(")").toString();
    }
}

public class RunningSum {

    public static void main(String[] args) {

        List<Long> numbers = Arrays.asList(2L, 2L, 4L, 5L);
        List<Long> tempRunningSum = new ArrayList<>();
        List<ValuePairs> result = new ArrayList<>();
        tempRunningSum.add(0L);
        numbers.stream().forEach(i -> {
            tempRunningSum.set(0, tempRunningSum.get(0) + i);
            result.add(new ValuePairs(i, tempRunningSum.get(0)));
        });

        System.out.println(result);
    }

}

Выход:

enter image description here

0 голосов
/ 19 марта 2019

Простая демонстрация -

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

public class RunningAdditionDemo{
     private static Integer total = 0;
     private String strOutput = new String();

     public static void main(String []args){
        RunningAdditionDemo demo = new RunningAdditionDemo();
        String output = demo.doRunningAddition();

        System.out.println(output);
     }

     public String doRunningAddition() {
        List<Integer> numbers = Arrays.asList(2, 2, 4, 5);
        numbers.stream().forEach(this::addNumber);

        return String.format("( %s )", strOutput.replaceFirst("..$",""));
     }

     private void addNumber(Integer number) {
        total += number;
        strOutput += String.format("( %d -> %d ), ", number, total);
     }
}
...