批量执行流操作

时间:2017-09-06 11:00:24

标签: java-stream

我有n个条目的有限流。提前计算未知。数据大小约为10Gb,并且适合RAM,因此我无法将其作为一个整体来阅读。 在每100000个条目之后,以块的形式处理该流的方法是什么?

Stream<?> blocks

因此我无法使用subList等方法。

我可以在代码中想象它是这样的:

    IntStream
            .range(0, Integer.MAX_VALUE)
            .filter(s -> s % 100000 == 0)
            .mapToObj(s -> blocks
                    .skip(s)
                    .collect(Collectors.toList())
                    .forEach(MyClass::doSomething)
            );

但后来我得到了错误,因为limit是终端运营商,它关闭了流。有一些解决方法吗?

2 个答案:

答案 0 :(得分:1)

如果

IntStream
        .range(0, Integer.MAX_VALUE)
        .filter(s -> s % 100000 == 0)
        .mapToObj(s -> blocks
                .skip(s)
                .collect(Collectors.toList())
                .forEach(MyClass::doSomething)
        );

编译时没有错误,方法doSomething必须是一次接收blocks单个元素的方法,就像List.forEach(…)所做的那样,单独为每个元素调用消费者。 (忽略List.forEachvoid的事实,因此对于外部流中的mapToObj(…)来说是不够的。在这种情况下,“批处理”没有任何好处,你可以使用

blocks.forEachOrdered(MyClass::doSomething);

因为这将一次加载一个元素并允许每个元素在处理下一个元素后处理后收集垃圾(除非doSomething在某处存储引用)。

您尝试在调用List之前将100000个元素收集到doSomething中并不会提高性能,因为流仍将依次加载每个元素并且您仍在处理一个元素。它只能防止最多99999个元素的垃圾收集,直到处理完第100000个元素。这没有优势。

答案 1 :(得分:0)

您似乎必须按照之前问题的答案中的建议使用Spliterator have a look at the Java docs

下面的简化示例将生成10个块块的输出,同时保持流打开。

Stream<Block<Integer>> blocks = IntStream
  .range(0, 1000)
  .mapToObj(Block::new);

Spliterator<Block<Integer>> split = blocks.spliterator();
int chunkSize = 10;

while (true) {
  List<Block<Integer>> chunk = new ArrayList<>(10);
  for (int i = 0; i < chunkSize && split.tryAdvance(chunk::add); i++) {
    chunk.get(i).doSomething();
  }
  System.out.println();
  if (chunk.isEmpty()) break;
}

class Block<T> {
  private T value;

  Block(T value) {
    this.value = value;
  }

  void doSomething() {
    System.out.print("v: " + value);
  }
}