【问题标题】:How to split Iterable in chunks with lambda expressions如何使用 lambda 表达式将 Iterable 拆分为块
【发布时间】:2018-01-20 04:02:06
【问题描述】:

我有 Iterable<CSVRecord> = CSVFormat.RFC4180.withFirstRecordAsHeader().parse(in)(阿帕奇公地) > 10.000.000.000 行的记录。以前我有带有计数器的 for 循环,并且在每 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放在哪里合适?

【问题讨论】:

  • records 是一个列表吗?如果是这样,也许您可​​以使用来自 guava 的 Lists.partition(Iterables 中也有一个版本)。
  • 更新了我的问题。调试器说记录是org.apache.commons.csv.CSVParser

标签: java lambda apache-commons iterable sublist


【解决方案1】:

我不确定你是否可以使用单个 lambda expressen 来完成,但你可以使用 .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())

【讨论】:

  • 那行得通。如果出现更好的解决方案,我会将问题留一天,但到目前为止,您的答案最适合我。谢谢!
【解决方案2】:

我认为最适合您的情况的解决方案是将数据转换阶段和分块分开。 对于数据转换(记录 -> 数组),您可以使用流或并行流。他们在这里闪耀。但是分块对于流来说不是一个好的场景,因为流可以一次生成一个块(通过跳过/限制)。所以你必须为每个块重新创建流。 最好使用简单的循环或一些库 api(如 RC 推荐的那样)。

【讨论】:

    【解决方案3】:

    这并不能回答分块的问题,但是...考虑购买更多内存。如果这是问题集的典型大小。 RAM 可能比持续编写内存效率高的程序所花费的额外思考时间成本更低。

    【讨论】:

    • 可能,但我喜欢编写内存效率高的程序:)
    • 你永远不会以这种态度在微软找到工作。
    【解决方案4】:

    您好,我不确定它是否看起来很棒,但这是另一种处理方式。

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

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 2015-03-08
      • 1970-01-01
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多