【问题标题】:Out of Memory while creating chunks of a very large list在创建非常大的列表块时内存不足
【发布时间】:2020-04-13 16:45:59
【问题描述】:

我需要获取公共 API 返回的所有成员的列表。

问题是我不知道任何 Id,但我知道第一个 Id 在 500000000 之后开始,最后一个 Id 大约在 570000000 左右。经过检查,我知道这些 Id 是生成的时间戳,但我没有任何其他信息。

所以我唯一的解决办法就是自己去获取它们。

这是我写的:

private List<List<Long>> getFinalList() {
    return chunkArrayList(getInitList(), 100);
}

private List<Long> getInitList() {
    List<Long> idList = new ArrayList<>();
    for (long i = 500000000L; i < 570000000L; i++) {
        idList.add(i);
    }
    return idList;
}

private List<List<Long>> chunkArrayList(List<Long> listToChunk, int chunkSize) {
    AtomicInteger counter = new AtomicInteger();
    return new ArrayList<>(listToChunk.stream().collect(Collectors.groupingBy(l -> counter.getAndIncrement() / chunkSize)).values());
}

注意:我必须将我的列表分成更小的列表,以便将它们分成多个 API 调用。

我的主要问题是,这段代码给我返回了一个内存不足的异常,这是不稳定的。

我怎样才能做到这样的过程,同时尽可能提高效率并避免这些内存问题?

【问题讨论】:

    标签: java list java-8 java-stream out-of-memory


    【解决方案1】:

    列出 70,000,000 个高度可预测的值有什么意义?如果您将分块列表传递给 API,则只需根据需要构建那些较小的列表。您只需要跟踪接下来要使用的号码。

    【讨论】:

    • 确实,它比我尝试做的更简单。
    【解决方案2】:

    您可以将返回类型从列表更改为流:

    private Stream<List<Long>> getFinalList() {
        return chunkArrayList(getInitList(), 100);
    }
    
    private Stream<Long> getInitList() {        
        return LongStream.rangeClosed(500000000L, 570000000L).boxed();
    }
    
    public Stream<List<Long>> chunkArrayList(Stream<Long> stream, int chunkSize) {
        AtomicInteger counter = new AtomicInteger(0);
        return stream.collect(Collectors.groupingBy(x -> counter.getAndIncrement() / chunkSize))
                .values().stream();
    }
    

    但正如@Zag 已经提到的那样,没有必要这样做。

    这样的事情可能会有所帮助

    public static void makeAnAPICallWithChunks(int chunkSize){
        LongStream.iterate(500000000L, x -> x + chunkSize)
                  .boxed()
                  .takeWhile(x -> x < 570000000L)
                  .forEach(x -> {System.out.println(x + " to " + (x + chunkSize-1));});
    }
    

    【讨论】:

    • 确实,@Zag 解决方案更好,我会选择那个选项。但只是出于好奇,流在内存管理方面是否更好?
    • @Synops Java 集合(Lists、Sets、Maps ..)是一种内存数据结构,所有元素都包含在内存中,而流是一种数据结构,所有元素都是按需计算的。 Java 流表示数据流经的管道以及对数据进行操作的函数。因此,它们可用于涉及数据驱动功能的任意数量的应用程序。与显式迭代(外部迭代)的集合相比,流操作为我们在幕后进行迭代。
    • 虽然大多数在线文献主要将 Java 8 流用于列表推导,但流也可用于描述对一组未知大小(可能无限)的数据的操作,这些数据不一定全部包含在记忆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-14
    • 1970-01-01
    • 2015-08-18
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多