带参数的Pattern.compile()

时间:2015-06-17 19:28:04

标签: java regex

说我有这段代码:

public static boolean checkRegex(String regex, String value) {
        if (value == null) return false;
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(value);
        return matcher.matches();
    }
.......

checkRegex("^.+@.+\\..+$", "email@example.com");

我的正则表达式会在编译时编译一次还是在运行时编译多次?

2 个答案:

答案 0 :(得分:4)

Pattern.compile(regex)只是一种对编译器没有特殊意义的方法,因此只能在运行时执行。虽然可以缓存已编译的模式(在其他语言中完成,例如Python),但Sun / Oracle的Pattern实现不会缓存已编译的Pattern,这意味着每次执行checkRegex方法(即使使用相同的正则表达式),您再次编译正则表达式并获得一个新的Pattern实例。

顺便说一下,你的方法

public static boolean checkRegex(String regex, String value) {
    if (value == null) return false;
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(value);
    return matcher.matches();
}

可以改写为

public static boolean checkRegex(String regex, String value) {
    if (value == null) 
        return false;
    return value.matches(regex);
}

答案 1 :(得分:1)

简短回答

虽然也许从理论的角度来看,不;不是在编译时,这些都是再次编译的。

但是Pattern使用 Flyweight 模式,这样一旦正则表达式被编译,它就会存储在内存中,因此不需要在运行时完全编译

编译时间

从理论的角度来看,编译器可能会执行所谓的constant propagation,从而在编译时解决问题。如果你调用的方法是final(或者在编译时已知被调用者)等等,这可以完成。

如果有人编译你的方法并检查 Java字节码,它会编译为:

public static boolean checkRegex(java.lang.String, java.lang.String);
    Code:
       0: aload_1       
       1: ifnonnull     6
       4: iconst_0      
       5: ireturn       
       6: aload_0       
       7: invokestatic  #15                 // Method java/util/regex/Pattern.compile:(Ljava/lang/String;)Ljava/util/regex/Pattern;
      10: astore_2      
      11: aload_2       
      12: aload_1       
      13: invokevirtual #16                 // Method java/util/regex/Pattern.matcher:(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
      16: astore_3      
      17: aload_3       
      18: invokevirtual #17                 // Method java/util/regex/Matcher.matches:()Z
      21: ireturn       

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #18                 // String ^.+@.+\..+$
       2: ldc           #19                 // String email@example.com
       4: invokestatic  #20                 // Method checkRegex:(Ljava/lang/String;Ljava/lang/String;)Z
       7: pop
       ...

正如您可能看到的那样,这些方法只是简单地转换为Java字节代码,并使用这两个参数调用该方法。

某些编译器(主要是函数和逻辑编程语言的编译器)允许程序专门化:如果使用常量完成某个调用,编译器可以创建该方法的副本并解决已知的问题在编译时。然而,它是一个权衡,因为引入了大量专门的方法,将减少大量的代码库。

运行时

Flyweight 模式

如果你深入研究java.util.regex.Pattern的Java字节码,你会看到:

private static final HashMap<String, CharPropertyFactory> map;

static CharProperty charPropertyFor(String name) {
        // <editor-fold defaultstate="collapsed" desc="Compiled Code">
        /* 0: getstatic     java/util/regex/Pattern$CharPropertyNames.map:Ljava/util/HashMap;
         * 3: aload_0
         * 4: invokevirtual java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
         * 7: checkcast     java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory
         * 10: astore_1
         * 11: aload_1
         * 12: ifnonnull     19
         * 15: aconst_null
         * 16: goto          23
         * 19: aload_1
         * 20: invokevirtual java/util/regex/Pattern$CharPropertyNames$CharPropertyFactory.make:()Ljava/util/regex/Pattern$CharProperty;
         * 23: areturn
         *  */
        // </editor-fold>
    }

请注意HashMap,这意味着Pattern存储正则表达式的片段及其相应的微型DFA,此行为称为Flyweight。这意味着您只需编译一次正则表达式。显然你仍然需要执行查找,所以这不是免费的优化,但它肯定会有所帮助。