【问题标题】:StringBuilder to Streams and Lambda expressionsStringBuilder 到 Streams 和 Lambda 表达式
【发布时间】:2021-09-30 14:55:35
【问题描述】:

我有一个方法:

public String tpeResponse(InputStream is) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder out = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            out.append(line);
        }
        reader.close();
        is.close();
        return out.toString();
}

在线:out.append(line); 我出错了:

java.lang.OutOfMemoryError: Java 堆空间

请帮助我使用 Streams 和 lambda 表达式重建此方法。

【问题讨论】:

  • 问题似乎是您要读取的文件太大,Java 应用程序无法在内存中保存。我不确定流或 lambda 表达式将如何帮助您仍然需要返回一个字符串,并且您生成它的任何方法都可能仍然会达到内存限制。如果可能,请增加限制或不将文件读入字符串。而是以块或移动指针或其他方式读取和处理它。
  • 既然你得到了OutOfMemoryError,我认为你遇到的问题比“使用流和 lambda 表达式编写”更大。
  • OutOfMemoryError 在数据太大时发生。增加你的堆大小,它会解决你的问题
  • 这对内存问题没有帮助。你需要做什么?
  • 您需要为BufferedReader reader = new BufferedReader(new InputStreamReader(is)) 使用Try-with-resources,以减少发生故障时的内存泄漏

标签: java lambda java-stream bufferedreader stringbuilder


【解决方案1】:

无论您从(传递到您的方法中的 is 变量)读取数据,都是

  • [A] 无止境(例如,如果它是 System.in,这是一个代表您的流程标准的 InputStream,并且您使用 java -jar yourapp.jar </dev/null 启动应用程序,那么它实际上是无止境的。

  • 或者,[B] 不是无穷无尽的,而是 LOT 的数据。例如,同样的情况,但你这样做java -jar yourapp.jar </my/20gigabyte4kCopyOfTheEntireLordOfTheRingsTrilogy.mp4

a StringBuilder 只是将所有数据存储在有限的进程内存中。限制到什么程度?嗯,当然不会超过您计算机中的 RAM 总量,但通常会少于此,这取决于您如何启动 JVM。如果你达到了这个限制,答案是不要使用 StringBuilder。

有几个解决方案。哪一个是正确的?从您在问题中提供的有限细节无法判断:

  • 意识到它是无限输入,因此根本不可能尝试存储所有数据。
  • 采用流式模型,不是将所有输入转换成一个大字符串然后处理该字符串,而是获取足够的输入,您可以对其进行操作,对其进行操作,然后继续。

例如,假设您有一个应用来计算“嘿”一词在输入流中出现的次数。

不要像以前那样写,而是可以这样做:

public int countHey(InputStream is) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    String line;

    int total = 0;
    while ((line = reader.readLine()) != null) {
        total += countHeyInLine(line);
    }
    reader.close();
    is.close(); // WARNING! SEE BELOW!
    return total;
}

即使你将收集到的莎士比亚作品提供给它,这段代码也能工作,因为这段代码读取一行,处理那一行,将此处理的结果减少为单个值,然后继续:Java 的垃圾收集器可以简单地收集您处理过的所有行,因此这段代码将愉快地挖掘您扔给它的所有数百万行,而不会耗尽内存。

注意:无论打开一个资源都应该关闭它,或者需要非常清楚地标记它会将关闭它的责任转移给调用它的人。您做了相反的事情:此方法不是创建该输入流的代码,但您正在关闭它。这是一个非常糟糕的主意,会导致资源泄漏。您不应该关闭 is 并在文档中明确说明您的代码没有。或者,如果你觉得这个方法最好关闭is,它应该在 try/finally 结构中这样做,这样调用者就可以放心,调用这个方法最终会以一种或另一种方式关闭该流。

【讨论】:

  • 那是一个文件,还是别的什么?你真的需要String吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-30
  • 2016-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多