【问题标题】:How to trim strings in java without creating a new object?java - 如何在不创建新对象的情况下修剪java中的字符串?
【发布时间】:2017-02-03 11:10:17
【问题描述】:

我有一个大文本文件(大约 2000 万行),其中包含以下格式的行:

<string1>, <string2>

现在这些字符串可能有尾随或前导空格,我想在读取文件时删除这些空格。

我目前正在为此使用trim(),但由于Java 中的String 是不可变的,trim() 正在为每个修剪操作创建一个新对象。 这会导致过多的内存浪费。

我怎样才能做得更好?

【问题讨论】:

  • 请说明您是如何读取文件然后拆分字符串的。
  • 您确实意识到所有未使用的字符串都会被收集,因此没有真正的浪费内存,只有新创建的对象(由 GC 有效收集)。
  • 我不太确定,但我认为使用sed 可以解决问题
  • 显示你用来读取文件的代码;几乎完全可以肯定,trim() 不会成为主要的内存瓶颈。
  • 用逗号分隔符分割你的字符串,然后,使用 StringBuilder 附加每个字符串。所以每次都不会像你说的那样创建字符串。

标签: java string trim


【解决方案1】:

如果不可变的String 类导致问题,我会感到惊讶; JVM 非常高效,是多年工程工作的结果。

也就是说,Java 确实提供了一个名为StringBuilder 的用于操作字符串的可变类。您可以阅读文档here

如果您正在跨线程工作,consider using StringBuffer

【讨论】:

    【解决方案2】:

    您可以将字符串读取为字符流,并记录您要解析的每个标记的开始和结束位置。

    这仍然会为每个令牌创建一个对象,但如果您的令牌相对较长,那么您的对象将包含的两个 int 字段将比相应的字符串小得多。

    但在您踏上这段旅程之前,您可能应该确保修剪的琴弦保留的时间不会超过需要的时间。

    【讨论】:

      【解决方案3】:

      假设您有一个包含&lt;string1&gt;, &lt;string2&gt;String,并且您只想拆分它而不修剪部分:

      String trimmedBetween(String str, int start, int end) {
        while (start < end && Character.isWhitespace(str.charAt(start)) {
          ++start;
        }
      
        while (start < end && Character.isWhitespace(str.charAt(end - 1)) {
          --end;
        }
      
        return str.substring(start, end);
      }
      

      (注意这基本上是String.trim() 的实现方式,只是使用startend 而不是0length

      然后像这样调用:

      int commaPos = str.indexOf(',');
      String firstString = trimmedBetween(str, 0, commaPos);
      String secondString = trimmedBetween(str, commaPos + 1, str.length());
      

      【讨论】:

      • 我确实想修剪部分,即单个字符串。
      • 我为什么要使用这个修剪而不是默认修剪?目标是避免内存浪费,但您使用与内置 trim() 相同的额外内存(= 返回一个新字符串)
      • 因为String.trim() 只修剪字符串的开头和结尾。要使用它,您必须拆分字符串(创建一个数组和两个字符串),然后修剪它们(最多两个字符串)。这种方法只创建了两个字符串,而不是 4 个字符串和一个数组。
      【解决方案4】:

      正如您已经注意到的,字符串是不可变的。所以解决方案是不使用字符串,而是使用可变的东西。 StringBuffer 是一个合适的类。

      但是,StringBuffer 不包含 trim 方法,因此您可以使用类似的方法:

      void trim(StringBuffer sb) {
          int start = 0;
          while (sb.length() > start && Character.isWhitespace(sb.charAt(0))) {
              start++;
          }
          sb.delete(0, start - 1);
      
          int end = 0;
          while (sb.length() > end && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
              end++;
          }
          sb.delete(sb.length() - end, sb.length() - 1);
      }
      

      【讨论】:

        【解决方案5】:

        如果您想避免String,则必须使用charStringBuilder 自己处理,如下所示:

        public class Test {
            public static void main(String... args) throws Exception {
                InputStreamReader in = new InputStreamReader(new FileInputStream("<testfile>"), "UTF-8");
        
                char[] buffer = new char[32768];
                int read = -1;
                int index;
                StringBuilder content = new StringBuilder();
                while ((read = in.read(buffer)) > -1) {
                    content.append(buffer, 0, read);
                    index = 0;
                    while (index > -1) {
                        index = content.indexOf("\n");
                        if (index > -1) {
                            char[] temp = new char[index];
                            content.getChars(0, index, temp, 0);
                            handleLine(temp);
                            content.replace(0, index + 1, "");
                        }
                    }
                }
        
                in.close();
            }
        
            private static void handleLine(char[] line) {
                StringBuilder content = new StringBuilder().append(line);
                int start = 0;
                int end = content.length();
                if (end > 0) {
                    char ch = content.charAt(0);
                    while (Character.isWhitespace(content.charAt(start))) {
                        start++;
                        if (end <= start) {
                            break;
                        }
                    }
                    if (start < end) {
                        while (Character.isWhitespace(content.charAt(end - 1))) {
                            end--;
                            if (end <= start) {
                                break;
                            }
                        }
                    }
                }
        
                System.out.println("***" + content.subSequence(start, end) + "***");
            }
        }
        

        【讨论】:

          【解决方案6】:

          我们可以通过正则表达式处理。

             {
              String str = "abcd, efgh";
              String [] result = str.split("(,\\s)|,");
              Arrays.asList(result).forEach(s -> System.out.println(s));
             }
          

          【讨论】:

            【解决方案7】:

            我认为您可以直接将结果数据写入新文件。

            String originStr = "   xxxxyyyy";
            for (int i = 0; i < originStr.length(); i++) {
                if (' ' == originStr.charAt(i)) {
                    continue;
                }
                NewFileOutPutStream.write(originStr.charAt(i));
            }
            

            【讨论】:

            • 如果你使用m线程模型,你可以将你的文件分开,让它们成为逻辑上的几个块文件,然后上面的方法也很好用。
            • 一次写一个字符会很费时间。你需要缓冲它。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-25
            • 2017-02-26
            • 2018-07-19
            • 1970-01-01
            • 2012-04-14
            • 2014-11-22
            相关资源
            最近更新 更多