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

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

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

只是为了好玩,我已经运行了这个微基准测试。 最后一次运行的结果(即后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和字符串是数字一样有效,但如果字符串不是数字,则非常慢。 正则表达式介于两者之间。

结论

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

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

 public class TestNumber { private final static List numbers = new ArrayList<>(); private final static List 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 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 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 list) { int sum = 0; for (String s : list) { try { Integer.parseInt(s); sum++; } catch (NumberFormatException e) { } } return sum; } } 

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

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

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

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

有关更多信息,请参阅FSM并查看基于Automata-based_programming

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

Integer.parseInt()

要么

Double.parseDouble()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

要具体回答你的问题:

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

看哪个更快。

答:正则表达式。