【问题标题】:Java regular expression offers any performance benefit?Java 正则表达式提供任何性能优势?
【发布时间】:2012-08-06 04:57:50
【问题描述】:

在 Java 中,当我们尝试使用正则表达式进行模式匹配时。例如获取输入字符串并使用正则表达式来确定它是否为数字。如果不是,则抛出异常。 在这种情况下,我理解,使用正则表达式使代码比我们获取字符串的每个字符,检查它是否是一个数字,如果不是抛出异常更简洁。

但我的假设是,正则表达式也会使过程更有效率。这是真的?我找不到关于这一点的任何证据。正则表达式如何在幕后进行匹配?它不是也遍历字符串并逐个检查每个字符吗?

【问题讨论】:

  • 简单的查找方法:运行两个选项并为每个选项计时。该过程受 CPU 限制,因此持续时间会告诉您哪个更有效。请注意,您可以通过重新使用已编译的模式来提高正则表达式的效率,而不是使用 string.matches(),后者会在每次调用时重新编译正则表达式。

标签: java regex performance


【解决方案1】:

只是为了好玩,我运行了这个微基准测试。上次运行的结果(即 JVM 预热/JIT)如下(无论如何,从一次运行到另一次运行的结果相当一致):

regex with numbers 123
chars with numbers 33
parseInt with numbers 33
regex with words 123
chars with words 34
parseInt with words 733

换句话说,chars 非常有效,如果字符串是数字,则 Integer.parseInt 与 char 一样有效,但如果字符串不是数字,则非常慢。正则表达式介于两者之间。

结论

如果您将字符串解析为数字,并且您希望字符串通常是数字,则使用 Integer.parseInt 是最佳解决方案(高效且可读)。当字符串不是数字时,如果不是太频繁,你得到的惩罚应该很低。

ps:我的正则表达式可能不是最佳的,请随时发表评论。

public class TestNumber {

    private final static List<String> numbers = new ArrayList<>();
    private final static List<String> words = new ArrayList<>();

    public static void main(String args[]) {
        long start, end;
        Random random = new Random();

        for (int i = 0; i < 1000000; i++) {
            numbers.add(String.valueOf(i));
            words.add(String.valueOf(i) + "x");
        }

        for (int i = 0; i < 5; i++) {
            start = System.nanoTime();
            regex(numbers);
            System.out.println("regex with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(numbers);
            System.out.println("chars with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(numbers);
            System.out.println("exceptions with numbers " + (System.nanoTime() - start) / 1000000);

            start = System.nanoTime();
            regex(words);
            System.out.println("regex with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(words);
            System.out.println("chars with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(words);
            System.out.println("exceptions with words " + (System.nanoTime() - start) / 1000000);
        }
    }

    private static int regex(List<String> list) {
        int sum = 0;
        Pattern p = Pattern.compile("[0-9]+");
        for (String s : list) {
            sum += (p.matcher(s).matches() ? 1 : 0);
        }
        return sum;
    }

    private static int chars(List<String> list) {
        int sum = 0;

        for (String s : list) {
            boolean isNumber = true;
            for (char c : s.toCharArray()) {
                if (c < '0' || c > '9') {
                    isNumber = false;
                    break;
                }
            }
            if (isNumber) {
                sum++;
            }
        }
        return sum;
    }

    private static int exception(List<String> list) {
        int sum = 0;

        for (String s : list) {
            try {
                Integer.parseInt(s);
                sum++;
            } catch (NumberFormatException e) {
            }
        }
        return sum;
    }
}

【讨论】:

  • 抛出和捕获异常通常是一项相当昂贵的操作。如果您确定格式是严格的数字,没有分组或小数分隔符,那么使用 char 方法可能是您可以实现的最快方法,尽管我会使用 Character.isDigit 而不是如果检查你上面有。如果您需要更强大的分组和小数分隔符支持,最好使用正则表达式或 NumberFormat 对象。
  • “抛出和捕获异常通常是一项相当昂贵的操作” 是的,但关键是当输入是数字时,parseInt 的速度一样快并且可以处理事情人们可能会忘记(签名等)。所以它更健壮和更快:没有理由不使用它,除非你知道你会得到很多会引发异常的输入。
  • 我同意,如果您不是为大量调用执行此操作,则 parseInt 可以,尽管我不确定 parseInt 是否处理分组分隔符等,其中 NumberFormat.parse()会。
【解决方案2】:

我还没有技术答案,但我可以写一些代码看看。 我不认为正则表达式是将字符串转换为数字的方法。在许多情况下,它们可能会更有效率,但如果写得不好,就会很慢。

但是我想问一下,你为什么不直接使用: Integer.parseInt("124")?这将引发 NumberFormatException。应该能够处理它,并且它将对数字的检测留给核心 Java。

【讨论】:

  • +1。尽管对于明显更长的数字字符串,即使 Long.parseLong 也会抛出 NumberFormatException。我不确定 Apache Commons 的 NumberUtils 是如何准确工作的,但是有一个名为 isDigits(String str) 的方法可以告诉您字符串是否是有效数字(至少根据 Java)。 commons.apache.org/lang/api-2.6/org/apache/commons/lang/math/…
  • 下面正则表达式的有趣结果。我也有兴趣查看不匹配的结果,并将其全部反转。取决于 Java 如何处理正则表达式。
【解决方案3】:

关于幕后的正则表达式...

有限状态机 (FSM) 等价于正则表达式。 FSM 是一台可以识别语言的机器(在您的情况下为数字)。 FSM 具有字母表、状态、初始状态、N 个最终状态以及从一种状态到另一种状态的转换函数。字符串需要包含在字母表中(例如 ASCII)。 FSM 从初始状态开始。当您输入一个字符串时,它会根据函数(状态,字符)=>状态从一个状态移动到另一个状态,逐个处理字符。当它达到最终状态时,您知道您的字符串是否为数字。

如需了解更多信息,请参阅FSMAutomata-based_programming

【讨论】:

    【解决方案4】:

    我不明白它怎么能比以下更简单或更容易阅读:

    Integer.parseInt()

    Double.parseDouble()

    它们完全按照您的描述进行,包括为无效输入抛出异常。

    关于性能:我希望正则表达式的效率低于上述。

    【讨论】:

      【解决方案5】:

      只要我的 5 美分 :) 一般来说,正则表达式语言不仅仅用于解析整数或字符串,它是一个非常强大的工具,可以识别任何“正则表达式”。它让我想起了我的大学时光(还记得 Automatas Theory 课程吗?:),但这里是 link,它描述了常规语言的真正含义

      现在由于它构建 FSM 引入了一些开销,所以对于Integer.parseInt 正则表达式引擎可能不是一个好的替代品,而且 Java 引入了更具体的 API。 然而,当我们处理更复杂的表达式并且我们有很多表达式时,正则表达式有好处。

      必须明智地使用正则表达式。 模式必须始终编译(否则不能有效地重用,因为每次编译模式都会消耗性能)

      我建议在更复杂的输入上运行测试,看看会发生什么。

      【讨论】:

        【解决方案6】:

        嗯,很难确定,但在一般情况下,与显式字符检查相比,正则表达式不太可能更有效。 RE 是最终状态自动机,因此自动机的构建和维护有一些开销。在我的实践中,显式代码总是比正则表达式更快(因此更高效)。

        但这是两难的。从交付时间的角度来看,正则表达式几乎总是更有效,并且在正确使用时更具可读性。这是另一个困境。我很少看到正确正则表达式的用法......

        在您的场景中,我建议使用番石榴库:

        boolean isValid = DIGIT.matchesAllOf("1234");
        

        【讨论】:

          【解决方案7】:

          最后,它确实在遍历字符串并检查每个字符,试图为提供的模式找到匹配项。此外,它使用回溯(如果有很多可能匹配的方法,引擎会尝试所有方法),这可能会导致某些不寻常情况下的性能非常差(您不太可能遇到这种情况,但理论上可能)。 java正则表达式引擎的最坏情况下的性能是O(2N),其中N是输入字符串的长度。

          有一些算法可以更快地提供 O(N) 性能,但与 Java 正则表达式相比功能更少。

          Here是一篇详细讨论这个问题的文章。

          但在大多数情况下,正则表达式引擎不会成为您应用程序的性能瓶颈。它足够快,所以通常不用担心它,除非你的分析器指向它。它提供了算法的声明性描述,这非常有用,因为几乎总是迭代算法实现会更冗长且可读性更低。

          【讨论】:

            【解决方案8】:

            具体回答您的问题:

            为什么不对一些复杂的文本应用正则表达式模式匹配,然后尝试自己编写相同的匹配代码。

            看看哪个更快。

            答案:正则表达式。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-11-18
              • 2018-11-09
              • 2013-11-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多