【问题标题】:How much slower does Java works on String than ArrayList?Java 在 String 上的工作比 ArrayList 慢多少?
【发布时间】:2015-05-13 17:04:24
【问题描述】:

在做 LeetCode 125 时,我采用了一个简单的算法:

  1. 修剪字符串。
  2. 遍历修剪后的字符串以验证它是否为回文。

修剪后的字符串存储为 String 或 ArrayList。但是,遍历修剪后的字符串(存储为字符串)导致“超时”,而遍历 ArrayList 被接受。由于除了 String/ArrayList 之外,这两段代码完全相同,我想知道 Java 处理 String 时它可能比 ArrayList 慢得多。

谁能告诉我差异来自哪里?

代码 1(按字符串):

public class Solution {
    public boolean isPalindrome(String s) {
        s = s.toLowerCase();
        String trimmed = "";
        for (int i=0; i<s.length(); i++) {
            char ch = s.charAt(i);
            if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') {
                trimmed = trimmed + ch;
            }
        }

        for (int i=0; i<(trimmed.length()+1)/2; i++) {
            if (trimmed.charAt(i) != trimed.charAt(trimmed.length() -1 -i)) {
                return false;
            }
        }
        return true;
    }
}

代码 2,(通过 ArrayList):

public class Solution {
    public boolean isPalindrome(String s) {
        s = s.toLowerCase();
        List<Character> trimmed = new ArrayList<Character>();
        for (int i=0; i<s.length(); i++) {
            char ch = s.charAt(i);
            if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') {
                trimmed.add(ch);
            }
        }

        for (int i=0; i<(trimmed.size())/2; i++) {
            if (trimmed.get(i) != trimmed.get(trimmed.size() - 1 -i)) {
                return false;
            }
        }
        return true;
    }
}

【问题讨论】:

  • 如果效率是主要关注点,您可以轻松地将字符验证融合到回文检测中,并完全避免创建任何临时对象(基本上从两端迭代,跳过被拒绝的字符)。
  • @Durandal。谢谢你的评论。我知道这个算法不够好,因为它可以通过遍历原始字符串一次来验证,而不是创建一个新的修剪过的字符串。我很好奇性能差异是怎么来的,以避免将来出现同样的陷阱。仍然感谢您的评论。

标签: java string performance arraylist jvm


【解决方案1】:

在这里:

    trimmed = trimmed + ch;

字符串连接并不便宜,所以每次你这样做时,你都会创建一个新的字符串并复制一个内部数组。在 ArrayList 示例中,当您编写时:

    trimmed.add(ch);

您不会每次都创建一个新数组。如果您希望字符串具有类似的性能,请使用 StringBuilder

StringBuilder trimmed = new StringBuilder();
    ...
    trimmed.append(ch)

对于您的情况,您可以考虑使用现有的 replace 方法替换所有空格:

String trimmed = s.replace(" ", "");

【讨论】:

  • 谢谢你,mk。我会参考 String 上的 Java 文档。
【解决方案2】:

字符串是不可变的,因此您的第一个示例是始终创建和丢弃大量实例。您至少应该使用StringBuilder 来组装这样的字符串。

【讨论】:

  • (我要补充一点,可以检查字节码以查看实际使用的内容,因为在某些情况下会使用StringBuilder。)
  • 非常感谢,chrylis。现在我知道在 String 上编码时应该改进哪一部分。
  • @DaveNewton 我非常怀疑任何编译器都会将循环优化为构建器;直到最近,一些编译器才开始使用它来进行单语句连接,而这种事情通常留给 JIT。
  • @chrylis 我认为他们明确在循环中执行此操作,它可能会为 concat 创建一个 StringBuilder 并将引用设置为 toString 结果.我的观点是,OP 可以检查代码以了解幕后发生的事情。
猜你喜欢
  • 2013-07-06
  • 2017-08-30
  • 2014-01-13
  • 2014-07-27
  • 2023-04-10
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多