总结列表中的每N个元素?

时间:2017-03-29 09:33:08

标签: java collections java-8 java-stream

我有一个这样的数字列表:

[ 0, 1, 2, 3, 4, 5, 6, 7 ]

如何以优雅的方式总结每个N(让我们假设2)元素并将列表转换为:

[ 1, 5, 9, 13 ]

修改 我提出了以下解决方案:

    List<Double> input = Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
    List<Double> output = new ArrayList<>();

    int N = 2;
    IntStream.range(0, (input.size() + N - 1) / N)
            .mapToObj(i -> input.subList(i * N, Math.min(N * (i + 1), input.size())))
            .mapToDouble(l -> l.stream().mapToDouble(Double::doubleValue).sum())
            .forEach(output::add);

    System.out.println(output);

它有效,但我仍然在寻找一种更易读,更简单的方法。

5 个答案:

答案 0 :(得分:3)

这是另一种有效的方法,无论您的元素是否已排序。此外,它不会期望任何有关您的起始值的信息,并且会在更广泛的范围内给出正确的结果。即使list.size() % n == 0条件不成立,此方法仍然有效。因此,从字面上看,它不需要任何先决条件即可获得所需的东西。

这种方法背后的直觉是您的索引是自然排序的,因此可以用来实现我们需要的内容。我们可以将元素归为一类,只要它们的当前索引i / n产生相同的值即可。然后,使用下游收集器来计算属于同一组的元素之和。看起来就是这样。

Collection<Integer> sumsOfnElements = IntStream.range(0, numbers.size()).boxed()
    .collect(Collectors.groupingBy(i -> i / n, 
        Collectors.summingInt(numbers::get))).values(); 
下面给出了遵循或多或少相同方法的等效迭代解。在性能严格的情况下,我会选择基于流的命令式解决方案。

假设:输入是一个整数数组,而不是一个列表。

final float inputLen = input.length;
final int resultLen = (int) Math.ceil(inputLen / n);
final int[] result = new int[resultLen];

for (int i = 0; i < inputLen; i++)
    result[i / n] += input[i];

答案 1 :(得分:2)

假设:

List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
int nth = 2;

怎么样:

IntStream.iterate(0, idx -> idx + nth)
         .limit(numbers.size() / nth)
         .map(idx -> IntStream.range(idx, idx + nth)
                              .reduce((sum, index) -> sum + numbers.get(index))
                              .orElse(0))
         .forEach(System.out::println);

或者:

IntStream.range(0, numbers.size() / nth)
         .map(idx -> IntStream.range(idx * nth, (idx + 1) * nth)
                              .map(index -> numbers.get(index))
                              .sum())
         .forEach(System.out::println);

答案 2 :(得分:1)

这是使用Collectors.groupingBy()的另一种方法:

List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
int N = 2;
Collection<Integer> sums =  numbers.stream()
                                   .collect(Collectors.groupingBy(i-> i/N,
                                                                  Collectors.summingInt(Integer::intValue)))
                                   .values();
System.out.println (sums);

输出:

[1, 5, 9, 13]

答案 3 :(得分:0)

这个怎么样? (我已编辑过 int nTh = 2; List<Integer> list = List.of(0, 1, 2, 3, 4, 5, 6, 7); int[] result = IntStream.iterate(0, i -> i + nTh) .limit(list.size() / nTh) .map(x -> { return IntStream.range(0, nTh).map(i -> list.get(i + x)).sum(); }) .toArray(); System.out.println(Arrays.toString(result));

ControlTemplate

答案 4 :(得分:0)

我认为最优雅的方法是使用flatMap,这样可以避免O(n²)的复杂性。 这是一个例子:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var myObject, myArray;
function myFunction(func,val) {
    var test = func.apply(test,val);
}
function myFunction2(val) {
    alert(val)
}
myObject = myFunction(myFunction2, ['myArray called using apply ']);      // Will return the value called from inside using `apply`
document.getElementById("demo").innerHTML = myObject; 
</script>

</body>
</html>

这将输出:

public static class Helper {

    private final int chunkSize;
    private int index;
    private int sum;

    public Helper(int chunkSize) {
        this.chunkSize = chunkSize;
    }

    public Stream<Integer> set(int v) {
        index += 1;
        sum += v;

        ArrayList<Integer> ret = new ArrayList<>();
        if (index == chunkSize) {
            ret.add(sum);
            sum = 0;
            index = 0;
        }
        return ret.stream();
    }
}

public static void main(String[] args) {
    List<Integer> input = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7);
    Helper helper = new Helper(2);
    List<Integer> output = input.stream().flatMap(helper::set).collect(Collectors.toList());

    System.out.println(input);
    System.out.println(output);
}