【问题标题】:Optimum time to perform an operation: within, or after loop执行操作的最佳时间:循环内或循环后
【发布时间】:2012-07-29 05:00:45
【问题描述】:

我正在读取一个文件以供稍后解析。该文件的大小不可能超过 MB,因此在现阶段这对我来说可能不是一个关键问题。但出于最佳实践的原因,我想知道何时是执行手术的最佳时间。

例子:

使用我从http://www.dzone.com/snippets/java-read-file-string 粘贴的方法,我正在将缓冲区读入字符串。我现在想删除所有空格。我的方法目前是这样的:

private String listRaw;

public boolean readList(String filePath) throws java.io.IOException {
    StringBuffer fileData = new StringBuffer(1024);
    BufferedReader reader = new BufferedReader(
            new FileReader(filePath));
    char[] buf = new char[1024];
    int numRead=0;
    while((numRead=reader.read(buf)) != -1){
        String readData = String.valueOf(buf, 0, numRead);
        fileData.append(readData);
        buf = new char[1024];
    }
    reader.close();
    listRaw = fileData.toString().replaceAll("\\s","");
    return true;
}

因此,我将字符串中的所有空格(完整地存储到类变量)中删除。

对我来说,这意味着更少的处理,但更多的内存使用。出于最佳实践原因,我将readData 变量附加到fileData 上时应用replaceAll() 操作会更好吗?使用更多处理,但避免传递多余的空白。

我想这对于像我正在处理的文件这样的小文件影响不大,但如果它是一个 200MB 的日志文件呢?

这完全取决于具体情况,还是有一个我最好遵循的共识?


感谢大家的意见。我相信您帮助我将思维定在了编写 Java 的正确方向。

我已更新我的代码以考虑提出的要点。包括 Don Roby 的建议,即在某些时候,我可能想保留空格。希望现在情况变得更好!

private String listRaw;

public boolean readList(String filePath) throws java.io.IOException {
    StringBuilder fileData = new StringBuilder(51200);
    BufferedReader reader = new BufferedReader(new FileReader(filePath));
    char[] buf = new char[51200];
    boolean spaced = false;
    while(reader.read(buf) != -1){
        for(int i=0;i<buf.length;i++) {
            char c = buf[i];
            if (c != '\t' && c != '\r' && c != '\n') {
                if (c == ' ') {
                    if (spaced) {
                        continue;
                    }
                    spaced = true;
                } else {
                    spaced = false;
                }

                fileData.append(c);
            }
        }
    }
    reader.close();
    listRaw = fileData.toString().trim();
    return true;
}

【问题讨论】:

  • 测量并找出答案!这样您就知道哪个实际上更快,而不是某人认为应该更快。
  • 好点!作为一个 Java 新手——目前还没有任何专门的 Java 教科书——我意识到从坏习惯开始。虽然我确信到一天结束时我会有很多。

标签: java performance optimization


【解决方案1】:

您最好只在最后创建和应用一次正则表达式替换。但是你会得到更多的

  • 以合理的大小初始化 StringBuilder
  • 避免在循环内创建字符串,并将读取的字符直接附加到 StringBuilder
  • 避免在每次迭代时实例化新的 char 缓冲区。

为避免不必要的长临时字符串创建,您可以逐个字符读取字符,并且仅将字符附加到 StringBuilder(如果它是not a whitespace)。最后,StringBuilder 将只包含好的字符,您不需要任何replaceAll() 调用。

【讨论】:

  • 谢谢!我现在要再看一遍。我已经为 stringbuilder 容量做了一个快速的谷歌,现在我正在翻垃圾。棒球场,将几千字节加载到缓冲区中是否完全可以?
  • 如果你的记忆允许的话,为什么不呢? StringBuilder 只是一个 char 数组的包装器。
【解决方案2】:

这段代码实际上有几个非常低效的地方,你必须先修复它们,然后再担心你提出的相对不那么重要的问题。

首先,不要在循环的每次迭代中创建新的buf 对象——使用相同的对象!这样做没有问题 - 新数据会覆盖旧数据,并且您可以节省对象分配(这是您可以执行的更昂贵的操作之一。)

其次,同样,不要创建String 来调用append()——使用append 的形式,它采用char 数组和偏移量(在本例中为0)和长度(@ 987654326@,在这种情况下。)同样,每次循环迭代都会少创建一个对象。

最后,回到您实际提出的问题:在循环中执行此操作会在每次迭代时创建一个 String 对象,但通过我们刚刚完成的调整,您将创建 zero 个对象每次迭代——因此删除循环末尾的空格是明显的赢家!

【讨论】:

  • 非常感谢您的 .append() 提示。只是习惯了用不同参数的同名方法覆盖方法,这对我来说有点陌生。看得出来我还有很长的路要走。谢谢!
【解决方案3】:

根据您要进行的解析,您最好在单独的步骤中删除空格,而在解析期间忽略它们。

想要删除 all 空格也是相当罕见的。您确定不想用单个空格替换多个空格吗?

【讨论】:

  • 是的,我想是的。我要解析的文件本质上是一个多维数组,用括号和键值对用等号分隔维度。没有字符串语句,只有名称和数值。
猜你喜欢
  • 1970-01-01
  • 2013-10-15
  • 2018-07-08
  • 2021-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多