Java 使用StringBuilder处理csv文件以节省堆空间

Java 使用StringBuilder处理csv文件以节省堆空间,java,heap,stringbuilder,Java,Heap,Stringbuilder,我正在读一个csv文件,它有大约50000行和1.1MiB大小,并且可以变大 在代码1中,我使用字符串处理csv,而在代码2中,我使用StringBuilder,只有一个线程执行代码,因此没有并发问题 使用StringBuilder比使用普通字符串类更难阅读代码 我是否过早地在Code2中使用StringBuilder进行优化以节省一点堆空间和内存 代码1 fr = new FileReader(file); BufferedReader read

我正在读一个csv文件,它有大约50000行和1.1MiB大小,并且可以变大

在代码1中,我使用字符串处理csv,而在代码2中,我使用StringBuilder,只有一个线程执行代码,因此没有并发问题

使用StringBuilder比使用普通字符串类更难阅读代码

我是否过早地在Code2中使用StringBuilder进行优化以节省一点堆空间和内存

代码1

            fr = new FileReader(file);
            BufferedReader reader = new BufferedReader(fr);

            String line = reader.readLine();
                while ( line != null )
                {
                    int separator = line.indexOf(',');
                    String symbol = line.substring(0, seperator);
                    int begin = separator;
                    separator = line.indexOf(',', begin+1);
                    String price = line.substring(begin+1, seperator);

                    // Publish this update
                    publisher.publishQuote(symbol, price);

                    // Read the next line of fake update data
                    line = reader.readLine();
                 }
代码2

编辑

我消除了toString调用,因此生成的字符串对象会更少

代码3

另外,原始代码是从

下载的,Code2的效率实际上低于Code1,因为每次调用stringBuilder.toString时,除了现有的stringBuilder对象之外,您还创建了一个新的java.lang.String实例。由于对象创建开销,这在空间和时间方面效率较低

将readLine的内容直接分配给字符串,然后拆分该字符串通常就足够了。您也可以考虑使用类。

节省内存提示

如果在输入中遇到多个重复令牌,请考虑使用String .In来确保每个相同的标记引用同一个字符串对象;e、 g

String[] tokens = parseTokens(line);
for (String token : tokens) {
  // Construct business object referencing interned version of token.
  BusinessObject bo = new BusinessObject(token.intern());
  // Add business object to collection, etc.
}
Code2的效率实际上不如Code1,因为每次调用stringBuilder.toString时,除了现有的stringBuilder对象之外,您还创建了一个新的java.lang.String实例。由于对象创建开销,这在空间和时间方面效率较低

将readLine的内容直接分配给字符串,然后拆分该字符串通常就足够了。您也可以考虑使用类。

节省内存提示

如果在输入中遇到多个重复令牌,请考虑使用String .In来确保每个相同的标记引用同一个字符串对象;e、 g

String[] tokens = parseTokens(line);
for (String token : tokens) {
  // Construct business object referencing interned version of token.
  BusinessObject bo = new BusinessObject(token.intern());
  // Add business object to collection, etc.
}
我是否过早地在Code2中使用StringBuilder进行优化以节省一点堆空间和内存

很可能:是的。但是,只有一种方法可以找到答案:分析代码

此外,我会使用合适的CSV解析器,而不是您现在正在做的:

我是否过早地在Code2中使用StringBuilder进行优化以节省一点堆空间和内存

很可能:是的。但是,只有一种方法可以找到答案:分析代码

此外,我会使用合适的CSV解析器,而不是您现在正在做的:

优化后的代码会提高应用程序的性能吗?-我的问题

第二个代码示例不会为您节省任何内存和计算时间。我担心您可能误解了StringBuilder的用途,它实际上是用于构建字符串,而不是读取字符串

在循环或第二个代码示例中,每一行都包含表达式stringBuilder.toString,本质上是将缓冲字符串反复转换为字符串对象。实际的字符串操作是针对这些对象执行的。不仅第一个代码示例更容易阅读,而且它的性能肯定与这两个示例相同

我是否过早地使用StringBuilder优化了内容你的问题

除非您分析了您的应用程序,并得出结论,即这些行会导致执行速度显著降低,否则是的。除非您真的确信某些事情会很慢,例如,如果您认识到高计算复杂度,那么在开始进行损害代码可读性的优化之前,您肯定希望进行一些分析

可以对此代码进行何种优化?-我的问题

如果您已经对应用程序进行了描述,并确定这是优化的正确位置,那么您应该考虑查看该类所提供的特性。实际上,这可能会给您带来更好的性能,而分析将告诉您这是否正确,并提供更简单的代码

优化后的代码会提高应用程序的性能吗?-我的问题

第二个代码示例不会为您节省任何内存和计算时间。我担心您可能误解了StringBuilder的用途,它实际上是用于构建字符串,而不是读取字符串

在循环或第二个代码示例中,每一行都包含表达式stringBuilder.toString,本质上是将缓冲字符串反复转换为字符串对象。实际的字符串操作是针对这些对象执行的。不仅第一个代码示例更容易阅读,而且它的性能肯定与这两个示例相同

我是否过早地使用StringBuilder优化了内容你的问题

除非您分析了您的应用程序,并得出结论,即这些行会导致执行速度显著降低,否则是的。除非你真的确信事情会很慢(如你 如果您认识到计算复杂度很高,那么在开始进行损害代码可读性的优化之前,您肯定希望进行一些评测

可以对此代码进行何种优化?-我的问题

如果您已经对应用程序进行了描述,并确定这是优化的正确位置,那么您应该考虑查看该类所提供的特性。实际上,这可能会给您带来更好的性能,分析会告诉您这是不是真的,更简单的代码。

通常是这样使用的:

StringBuilder sb = new StringBuilder();
sb.append("You").append(" can chain ")
  .append(" your ").append(" strings ")
  .append("for better readability.");

String myString = sb.toString(); // only call once when you are done
System.out.prinln(sb); // also calls sb.toString().. print myString instead
通常是这样使用的:

StringBuilder sb = new StringBuilder();
sb.append("You").append(" can chain ")
  .append(" your ").append(" strings ")
  .append("for better readability.");

String myString = sb.toString(); // only call once when you are done
System.out.prinln(sb); // also calls sb.toString().. print myString instead

StringBuilder有几个优点

StringBuffer的操作是同步的,但StringBuilder不是同步的,因此使用StringBuilder将提高单线程场景中的性能 一旦缓冲区被扩展,就可以通过调用对象上的setLength0重用缓冲区。有趣的是,如果您进入调试器并检查StringBuilder的内容,您将看到即使在调用setLength0之后,内容仍然存在。JVM只是重置字符串开头的指针。下次开始追加字符时,指针会移动 如果您不确定字符串的长度,最好使用StringBuilder,因为一旦扩展了缓冲区,就可以将相同的缓冲区用于更小或相等的大小 StringBuffer和StringBuilder在所有操作中几乎相同,只是StringBuffer是同步的,StringBuilder不是


如果没有多线程,那么最好使用StringBuilder。StringBuilder有几个优点

StringBuffer的操作是同步的,但StringBuilder不是同步的,因此使用StringBuilder将提高单线程场景中的性能 一旦缓冲区被扩展,就可以通过调用对象上的setLength0重用缓冲区。有趣的是,如果您进入调试器并检查StringBuilder的内容,您将看到即使在调用setLength0之后,内容仍然存在。JVM只是重置字符串开头的指针。下次开始追加字符时,指针会移动 如果您不确定字符串的长度,最好使用StringBuilder,因为一旦扩展了缓冲区,就可以将相同的缓冲区用于更小或相等的大小 StringBuffer和StringBuilder在所有操作中几乎相同,只是StringBuffer是同步的,StringBuilder不是


如果没有多线程,那么最好使用StringBuilder

+1,代码似乎误用了StringBuilder实现1更干净,可能更好。@在上面的代码3中,我删除了StringBuilder.toString调用,这应该会减少创建的字符串对象数。code1使用String类中的功能而不是csv解析器的原因是为了尽量减少堆中的对象数量,因为根据您的经验,此代码在java实时vm上运行,Scanner类的性能会更好吗?@por:我在java中对性能关键的字符串操作的经验不是很全面。如果您真的认为这很重要,您可以在紧密循环中尝试并测量吞吐量。+1,代码似乎误用了StringBuilder实现1更干净,可能更好。@Jørn在上面的代码3中,我删除了StringBuilder.toString调用,这应该会减少创建的字符串对象的数量。code1使用String类中的功能而不是csv解析器的原因是为了尽量减少堆中的对象数量,因为根据您的经验,此代码在java实时vm上运行,Scanner类的性能会更好吗?@por:我在java中对性能关键的字符串操作的经验不是很全面。如果您真的认为这很重要,那么您可以在紧密循环中尝试这两种方法并测量吞吐量。+1请注意,@portoalet,字符串实习不能保证产生任何额外的性能。至少使用秒表进行一些评测,以检查它是否对您的情况有帮助:+1但请注意,@portoalet,不保证字符串实习产生任何额外性能。至少使用秒表进行一些评测,以检查它是否对您的情况有帮助:+1以推荐一个真正的解析器。给出的代码以引号和逗号作为实际值失败。+1我在生产环境中使用ostermiller csv/excelcsv解析器/打印机,这非常好。+1用于推荐真正的解析器。给定的代码失败,实际值为引号和逗号。+1我在生产环境中使用ostermiller csv/excelcsv解析器/打印机,这非常好。即使没有toString调用,Code1也会更有效。为什么?由于String是不可变的,因此String.substring调用将共享底层char[],这是一个比String类本身更大的内存问题。StringBuilder.substring调用必须复制字符[],因为StringBuilder是可变的。因此,Code3涉及更多的字符复制和更多的字符[]实例化 我会更有效率。为什么?由于String是不可变的,因此String.substring调用将共享底层char[],这是一个比String类本身更大的内存问题。StringBuilder.substring调用必须复制字符[],因为StringBuilder是可变的。因此,代码3涉及更多的字符复制和更多的字符[]实例化。