Java正则表达式提供了任何性能优势?

时间:2012-08-09 01:07:37

标签: java regex performance

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

但我假设正则表达式也使这​​个过程更有效率。这是真的?我在这一点上找不到任何证据。正则表达式如何在幕后进行比赛?是不是也在迭代字符串并逐个检查每个字符?

8 个答案:

答案 0 :(得分:4)

为了好玩,我已经运行了这个微观基准。最后一次运行的结果(即后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

换句话说,字符非常有效,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;
    }
}

答案 1 :(得分:3)

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

但是,请问您为什么不使用: Integer.parseInt("124")?这将抛出NumberFormatException。应该能够处理它,并且它将检测到一个数字直到核心Java。

答案 2 :(得分:1)

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

有限状态机(FSM)等同于正则表达式。 FSM是一种可识别语言的机器(在您的案例编号中)。 FSM具有字母,状态,初始状态,N-final状态以及从一个状态到另一个状态的转换函数。字符串需要包含在字母表中(例如ASCII)。 FSM从初始状态开始。当你输入一个字符串时,它会根据一个函数(state,char)=&gt;从char状态移动到另一个状态。州。当它到达最终状态时,您知道字符串是否为数字。

有关详情,请参阅FSM并查看Automata-based_programming

答案 3 :(得分:1)

我不知道它是如何变得更简单或更容易阅读:

Integer.parseInt()

Double.parseDouble()

他们完全按照您的描述执行操作,包括为无效输入抛出异常。

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

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:0)

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

但这就是困境。从正确到交付的角度来看,正则表达式几乎总是更有效,正确使用时,正确的表达式。这是另一个困境。我很少看到正确使用正则表达式...

在您的场景中,我建议使用guava库:

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

答案 6 :(得分:0)

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

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

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

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

答案 7 :(得分:0)

具体回答你的问题:

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

看哪个更快。

答案:正则表达式。