Java 对列表中的每N个元素求和?

Java 对列表中的每N个元素求和?,java,lambda,collections,java-8,java-stream,Java,Lambda,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<>();

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

[ 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);
List input=Arrays.asList(0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0);
列表输出=新的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(输出::添加);
系统输出打印项次(输出);
它可以工作,但我仍在寻找一个更具可读性和更简单的方法。

这个怎么样?(我已编辑以获取第n个)

intn=2;
列表=列表(0,1,2,3,4,5,6,7);
int[]result=IntStream.iterate(0,i->i+n)
.limit(list.size()/nTh)
.map(x->{
返回IntStream.range(0,n).map(i->list.get(i+x)).sum();
})
.toArray();
System.out.println(Arrays.toString(result));

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

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);
}
鉴于:

或者:

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

下面是另一种方法,使用
收集器。groupingBy()


这里有另一种方法,不管您的元素是否已排序,都可以使用。此外,它不会对您的初始值进行任何预测,并在更广的范围内给出正确的结果。即使
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(); 
Collection sumsofnements=IntStream.range(0,numbers.size()).boxed()
.collect(收集器).groupingBy(i->i/n,
Collectors.summingit(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];
final float inputLen=input.length;
final int resultLen=(int)Math.ceil(inputLen/n);
最终int[]结果=新int[resultLen];
for(int i=0;i
您为此尝试过什么吗?在尝试提问时发布代码是一种很好的做法。如果数组始终是算术序列,那么一种优雅的方法就是使用算术序列和的公式。在您的例子中,您必须稍微使用该公式,因为您必须对
a
b
之间的数字求和(这是算术序列的子序列)。请参阅。然后,您可以对每个区块求和以得到最终结果。对于给定的示例,这只适用于n=2和n=4。@Edd我假设
list.size
确实可以被
n
整除;但这就是提供的例子…我可以理解,但这不会运行任何n次。如果您只想介绍提供的示例,那么我认为最好删除提及N的内容,因为它会误导人们相信它适用于任何N[0,l.size),事实并非如此。由您决定。非常干净的解决方案!看起来现在应该是一个可接受的答案。lambdas的使用非常好。我喜欢“严格的性能设置"位。如果性能是个问题,parallelStream会有帮助吗?或者说开销不值得吗?好吧,既然基于流的实现是并行友好的,它应该会产生明显的并行加速。但是我们不应该忘记并行执行带来的启动和合并成本。最好的办法是通过做一个测试来衡量它标记不同大小的数据集。有时加速增益取决于许多因素,包括数据集的大小、内存位置等。无论如何,基于流的解决方案肯定比其顺序对应的解决方案更具可扩展性。了解原始性能和可销售性之间的差异总是很有趣的。
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);
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]
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];