如何使用 lambda 表达式将 Iterable 分成块
How to split Iterable in chunks with lambda expressions
我有
Iterable<CSVRecord> = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in)
(阿帕奇公地)
记录 > 10.000.000.000 行。以前我有一个带计数器的循环,在每 x 行之后我正在处理数据。现在我尝试用 Java 8 个 lambda 表达式实现类似的效果。
到目前为止我想出了这个,但是它耗尽了内存,因为我无法找到如何在 subList
中拆分它的正确方法
Iterable<List<?>> params = new ArrayList<>(StreamSupport
.stream(records.spliterator(), true)
.map(r -> Arrays.asList(
r.get("name"),
r.get("surname"),
r.get("something"),
))
.collect(Collectors.toList()).subList(0, 20000));
末尾的子列表不起作用:(
我只需要一些关于如何拆分的概念证明 Iterable
- 例如正确的地方放在哪里 subList
?
我不确定你是否可以使用单个 lambda 表达式来完成,但你可以使用 .skip()
和 .limit()
:
int maxSize = 20000;
int currentOffset = 0; // Increment by maxSize each iteration
Iterable<List<?>> params = new ArrayList<>(StreamSupport
.stream(records.spliterator(), true)
.skip(currentOffset)
.limit(maxSize)
.map(r -> Arrays.asList(
r.get("name"),
r.get("surname"),
r.get("something"),
))
.collect(Collectors.toList())
我认为适合您的情况的最佳解决方案是将数据转换阶段和分块分开。
对于数据转换(记录 -> 数组),您可以使用流或并行流。他们在这里闪耀。但是分块对于流来说不是一个好的场景,因为流一次可以产生一个块(通过 skip/limit)。所以你必须为每个块重新创建流。
最好使用简单循环或某些库 api(如 RC 推荐的那样)。
这没有回答分块的问题,但是...考虑购买更多内存。如果这是问题集的典型大小。 RAM 可能比持续编写内存高效程序的额外思考时间成本更便宜。
嗨,我不确定它是否看起来不错,但这是另一种处理方式。
//that can be CsvParser not List
List<Integer> collection = Arrays.asList(1, 2, 4, 5, 2, 1, 2, 4, 5);
int limit = 2;
int size = collection.size();
long maxPartIndex = (long) Math.ceil((double) size/ limit);
LongStream.range(0, maxPartIndex)
.mapToObj(partIndex -> getPart(collection.spliterator(), partIndex, limit))
.forEach(System.out::println);
.....
private static <T> List<T> getPart(Spliterator<T> stream, long index, long size) {
return StreamSupport.stream(stream, false)
.skip(index * size)
.limit(size)
.collect(Collectors.toList());
}
输出:
(1, 2)
(4, 5)
(2, 1)
(2, 4)
(5)
我有
Iterable<CSVRecord> = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in)
(阿帕奇公地)
记录 > 10.000.000.000 行。以前我有一个带计数器的循环,在每 x 行之后我正在处理数据。现在我尝试用 Java 8 个 lambda 表达式实现类似的效果。
到目前为止我想出了这个,但是它耗尽了内存,因为我无法找到如何在 subList
Iterable<List<?>> params = new ArrayList<>(StreamSupport
.stream(records.spliterator(), true)
.map(r -> Arrays.asList(
r.get("name"),
r.get("surname"),
r.get("something"),
))
.collect(Collectors.toList()).subList(0, 20000));
末尾的子列表不起作用:(
我只需要一些关于如何拆分的概念证明 Iterable
- 例如正确的地方放在哪里 subList
?
我不确定你是否可以使用单个 lambda 表达式来完成,但你可以使用 .skip()
和 .limit()
:
int maxSize = 20000;
int currentOffset = 0; // Increment by maxSize each iteration
Iterable<List<?>> params = new ArrayList<>(StreamSupport
.stream(records.spliterator(), true)
.skip(currentOffset)
.limit(maxSize)
.map(r -> Arrays.asList(
r.get("name"),
r.get("surname"),
r.get("something"),
))
.collect(Collectors.toList())
我认为适合您的情况的最佳解决方案是将数据转换阶段和分块分开。 对于数据转换(记录 -> 数组),您可以使用流或并行流。他们在这里闪耀。但是分块对于流来说不是一个好的场景,因为流一次可以产生一个块(通过 skip/limit)。所以你必须为每个块重新创建流。 最好使用简单循环或某些库 api(如 RC 推荐的那样)。
这没有回答分块的问题,但是...考虑购买更多内存。如果这是问题集的典型大小。 RAM 可能比持续编写内存高效程序的额外思考时间成本更便宜。
嗨,我不确定它是否看起来不错,但这是另一种处理方式。
//that can be CsvParser not List
List<Integer> collection = Arrays.asList(1, 2, 4, 5, 2, 1, 2, 4, 5);
int limit = 2;
int size = collection.size();
long maxPartIndex = (long) Math.ceil((double) size/ limit);
LongStream.range(0, maxPartIndex)
.mapToObj(partIndex -> getPart(collection.spliterator(), partIndex, limit))
.forEach(System.out::println);
.....
private static <T> List<T> getPart(Spliterator<T> stream, long index, long size) {
return StreamSupport.stream(stream, false)
.skip(index * size)
.limit(size)
.collect(Collectors.toList());
}
输出:
(1, 2) (4, 5) (2, 1) (2, 4) (5)